difftreelog
Merge pull request #647 from UniqueNetwork/feature/supports-interface-for-erc721-metadata
in: master
57 files changed
.maintain/scripts/generate_sol.shdiffbeforeafterboth11formatted=$(mktemp)11formatted=$(mktemp)12prettier --config $PRETTIER_CONFIG $raw > $formatted12prettier --config $PRETTIER_CONFIG $raw > $formatted1314sed -i -E -e "s/.+\/\/ FORMATTING: FORCE NEWLINE//g" $formatted131514mv $formatted $OUTPUT16mv $formatted $OUTPUT1517crates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth291291292struct MethodInfo {292struct MethodInfo {293 rename_selector: Option<String>,293 rename_selector: Option<String>,294 hide: bool,294}295}295impl Parse for MethodInfo {296impl Parse for MethodInfo {296 fn parse(input: ParseStream) -> syn::Result<Self> {297 fn parse(input: ParseStream) -> syn::Result<Self> {297 let mut rename_selector = None;298 let mut rename_selector = None;299 let mut hide = false;300 while !input.is_empty() {298 let lookahead = input.lookahead1();301 let lookahead = input.lookahead1();299 if lookahead.peek(kw::rename_selector) {302 if lookahead.peek(kw::rename_selector) {300 let k = input.parse::<kw::rename_selector>()?;303 let k = input.parse::<kw::rename_selector>()?;305 {308 {306 return Err(syn::Error::new(k.span(), "rename_selector is already set"));309 return Err(syn::Error::new(k.span(), "rename_selector is already set"));307 }310 }308 }311 } else if lookahead.peek(kw::hide) {312 input.parse::<kw::hide>()?;313 hide = true;314 } else {315 return Err(lookahead.error());316 }317318 if input.peek(Token![,]) {319 input.parse::<Token![,]>()?;320 } else if !input.is_empty() {321 return Err(syn::Error::new(input.span(), "expected end"));322 }323 }309 Ok(Self { rename_selector })324 Ok(Self {325 rename_selector,326 hide,327 })310 }328 }311}329}548 syn::custom_keyword!(expect_selector);566 syn::custom_keyword!(expect_selector);549567550 syn::custom_keyword!(rename_selector);568 syn::custom_keyword!(rename_selector);569 syn::custom_keyword!(hide);551}570}552571553/// Rust methods are parsed into this structure when Solidity code is generated572/// Rust methods are parsed into this structure when Solidity code is generated558 screaming_name: Ident,577 screaming_name: Ident,559 selector_str: String,578 selector_str: String,560 selector: u32,579 selector: u32,580 hide: bool,561 args: Vec<MethodArg>,581 args: Vec<MethodArg>,562 has_normal_args: bool,582 has_normal_args: bool,563 has_value_args: bool,583 has_value_args: bool,570 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {590 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {571 let mut info = MethodInfo {591 let mut info = MethodInfo {572 rename_selector: None,592 rename_selector: None,593 hide: false,573 };594 };574 let mut docs = Vec::new();595 let mut docs = Vec::new();575 let mut weight = None;596 let mut weight = None;667 screaming_name: snake_ident_to_screaming(ident),688 screaming_name: snake_ident_to_screaming(ident),668 selector_str,689 selector_str,669 selector,690 selector,691 hide: info.hide,670 args,692 args,671 has_normal_args,693 has_normal_args,672 has_value_args,694 has_value_args,826 let docs = &self.docs;848 let docs = &self.docs;827 let selector_str = &self.selector_str;849 let selector_str = &self.selector_str;828 let selector = self.selector;850 let selector = self.selector;851 let hide = self.hide;829 let is_payable = self.has_value_args;852 let is_payable = self.has_value_args;830 quote! {853 quote! {831 SolidityFunction {854 SolidityFunction {832 docs: &[#(#docs),*],855 docs: &[#(#docs),*],833 selector_str: #selector_str,856 selector_str: #selector_str,834 selector: #selector,857 selector: #selector,858 hide: #hide,835 name: #camel_name,859 name: #camel_name,836 mutability: #mutability,860 mutability: #mutability,837 is_payable: #is_payable,861 is_payable: #is_payable,crates/evm-coder/src/solidity.rsdiffbeforeafterboth225225226pub trait SolidityArguments {226pub trait SolidityArguments {227 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;227 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;228 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;228 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result;229 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;229 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;230 fn is_empty(&self) -> bool {230 fn is_empty(&self) -> bool {231 self.len() == 0231 self.len() == 0248 Ok(())248 Ok(())249 }249 }250 }250 }251 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {251 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {252 Ok(())252 Ok(())253 }253 }254 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {254 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {283 Ok(())283 Ok(())284 }284 }285 }285 }286 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {286 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {287 writeln!(writer, "\t\t{};", self.0)287 writeln!(writer, "\t{prefix}\t{};", self.0)288 }288 }289 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {289 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {290 T::solidity_default(writer, tc)290 T::solidity_default(writer, tc)318 Ok(())318 Ok(())319 }319 }320 }320 }321 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {321 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {322 writeln!(writer, "\t\t{};", self.1)322 writeln!(writer, "\t{prefix}\t{};", self.1)323 }323 }324 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {324 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {325 T::solidity_default(writer, tc)325 T::solidity_default(writer, tc)337 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {337 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {338 Ok(())338 Ok(())339 }339 }340 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {340 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {341 Ok(())341 Ok(())342 }342 }343 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {343 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {365 )* );365 )* );366 Ok(())366 Ok(())367 }367 }368 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {368 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {369 for_tuples!( #(369 for_tuples!( #(370 Tuple.solidity_get(writer)?;370 Tuple.solidity_get(prefix, writer)?;371 )* );371 )* );372 Ok(())372 Ok(())373 }373 }418 pub docs: &'static [&'static str],418 pub docs: &'static [&'static str],419 pub selector_str: &'static str,419 pub selector_str: &'static str,420 pub selector: u32,420 pub selector: u32,421 pub hide: bool,421 pub name: &'static str,422 pub name: &'static str,422 pub args: A,423 pub args: A,423 pub result: R,424 pub result: R,431 writer: &mut impl fmt::Write,432 writer: &mut impl fmt::Write,432 tc: &TypeCollector,433 tc: &TypeCollector,433 ) -> fmt::Result {434 ) -> fmt::Result {435 let hide_comment = self.hide.then(|| "// ").unwrap_or("");434 for doc in self.docs {436 for doc in self.docs {435 writeln!(writer, "\t///{}", doc)?;437 writeln!(writer, "\t{hide_comment}///{}", doc)?;436 }438 }437 writeln!(439 writeln!(438 writer,440 writer,439 "\t/// @dev EVM selector for this function is: 0x{:0>8x},",441 "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",440 self.selector442 self.selector441 )?;443 )?;442 writeln!(writer, "\t/// or in textual repr: {}", self.selector_str)?;444 writeln!(445 writer,446 "\t{hide_comment}/// or in textual repr: {}",447 self.selector_str448 )?;443 write!(writer, "\tfunction {}(", self.name)?;449 write!(writer, "\t{hide_comment}function {}(", self.name)?;444 self.args.solidity_name(writer, tc)?;450 self.args.solidity_name(writer, tc)?;445 write!(writer, ")")?;451 write!(writer, ")")?;446 if is_impl {452 if is_impl {463 }469 }464 if is_impl {470 if is_impl {465 writeln!(writer, " {{")?;471 writeln!(writer, " {{")?;466 writeln!(writer, "\t\trequire(false, stub_error);")?;472 writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;467 self.args.solidity_get(writer)?;473 self.args.solidity_get(hide_comment, writer)?;468 match &self.mutability {474 match &self.mutability {469 SolidityMutability::Pure => {}475 SolidityMutability::Pure => {}470 SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,476 SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,471 SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,477 SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,472 }478 }473 if !self.result.is_empty() {479 if !self.result.is_empty() {474 write!(writer, "\t\treturn ")?;480 write!(writer, "\t{hide_comment}\treturn ")?;475 self.result.solidity_default(writer, tc)?;481 self.result.solidity_default(writer, tc)?;476 writeln!(writer, ";")?;482 writeln!(writer, ";")?;477 }483 }478 writeln!(writer, "\t}}")?;484 writeln!(writer, "\t{hide_comment}}}")?;479 } else {485 } else {480 writeln!(writer, ";")?;486 writeln!(writer, ";")?;481 }487 }488 if self.hide {489 writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;490 }482 Ok(())491 Ok(())483 }492 }484}493}pallets/common/src/dispatch.rsdiffbeforeafterboth9 traits::Get,9 traits::Get,10};10};11use sp_runtime::DispatchError;11use sp_runtime::DispatchError;12use up_data_structs::{CollectionId, CreateCollectionData};12use up_data_structs::{CollectionId, CreateCollectionData, CollectionFlags};131314use crate::{pallet::Config, CommonCollectionOperations, CollectionHandle};14use crate::{pallet::Config, CommonCollectionOperations, CollectionHandle};151580 sender: T::CrossAccountId,80 sender: T::CrossAccountId,81 payer: T::CrossAccountId,81 payer: T::CrossAccountId,82 data: CreateCollectionData<T::AccountId>,82 data: CreateCollectionData<T::AccountId>,83 flags: CollectionFlags,83 ) -> Result<CollectionId, DispatchError>;84 ) -> Result<CollectionId, DispatchError>;848585 /// Delete the collection.86 /// Delete the collection.pallets/common/src/erc.rsdiffbeforeafterboth592 ///592 ///593 /// @dev Owner can be changed only by current owner593 /// @dev Owner can be changed only by current owner594 /// @param newOwner new owner account594 /// @param newOwner new owner account595 #[solidity(rename_selector = "changeCollectionOwner")]595 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {596 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {596 self.consume_store_writes(1)?;597 self.consume_store_writes(1)?;597598660 pub mod key {661 pub mod key {661 use super::*;662 use super::*;662663 /// Key "schemaName".664 pub fn schema_name() -> up_data_structs::PropertyKey {665 property_key_from_bytes(b"schemaName").expect(EXPECT_CONVERT_ERROR)666 }667663668 /// Key "baseURI".664 /// Key "baseURI".669 pub fn base_uri() -> up_data_structs::PropertyKey {665 pub fn base_uri() -> up_data_structs::PropertyKey {672668673 /// Key "url".669 /// Key "url".674 pub fn url() -> up_data_structs::PropertyKey {670 pub fn url() -> up_data_structs::PropertyKey {675 property_key_from_bytes(b"url").expect(EXPECT_CONVERT_ERROR)671 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)676 }672 }677673678 /// Key "suffix".674 /// Key "suffix".679 pub fn suffix() -> up_data_structs::PropertyKey {675 pub fn suffix() -> up_data_structs::PropertyKey {680 property_key_from_bytes(b"suffix").expect(EXPECT_CONVERT_ERROR)676 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)681 }677 }682678683 /// Key "parentNft".679 /// Key "parentNft".686 }682 }687 }683 }688689 /// Values.690 pub mod value {691 use super::*;692693 /// Value "ERC721Metadata".694 pub const ERC721_METADATA: &[u8] = b"ERC721Metadata";695696 /// Value for [`ERC721_METADATA`].697 pub fn erc721() -> up_data_structs::PropertyValue {698 property_value_from_bytes(ERC721_METADATA).expect(EXPECT_CONVERT_ERROR)699 }700 }701684702 /// Convert `byte` to [`PropertyKey`].685 /// Convert `byte` to [`PropertyKey`].703 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {686 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {pallets/common/src/lib.rsdiffbeforeafterboth71 Collection,71 Collection,72 RpcCollection,72 RpcCollection,73 CollectionFlags,73 CollectionFlags,74 RpcCollectionFlags,74 CollectionId,75 CollectionId,75 CreateItemData,76 CreateItemData,76 MAX_TOKEN_PREFIX_LENGTH,77 MAX_TOKEN_PREFIX_LENGTH,825 properties,826 properties,826 read_only: flags.external,827 read_only: flags.external,828829 flags: RpcCollectionFlags {827 foreign: flags.foreign,830 foreign: flags.foreign,831 erc721metadata: flags.erc721metadata,832 },828 })833 })829 }834 }830}835}pallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/lib.rsdiffbeforeafterboth212 owner: T::CrossAccountId,212 owner: T::CrossAccountId,213 payer: T::CrossAccountId,213 payer: T::CrossAccountId,214 data: CreateCollectionData<T::AccountId>,214 data: CreateCollectionData<T::AccountId>,215 flags: CollectionFlags,215 ) -> Result<CollectionId, DispatchError> {216 ) -> Result<CollectionId, DispatchError> {216 <PalletCommon<T>>::init_collection(owner, payer, data, CollectionFlags::default())217 <PalletCommon<T>>::init_collection(owner, payer, data, flags)217 }218 }218219219 /// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.220 /// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth18}18}191920/// @title A contract that allows you to work with collections.20/// @title A contract that allows you to work with collections.21/// @dev the ERC-165 identifier for this interface is 0x3e1e808321/// @dev the ERC-165 identifier for this interface is 0x62e2229022contract Collection is Dummy, ERC165 {22contract Collection is Dummy, ERC165 {23 /// Set collection property.23 /// Set collection property.24 ///24 ///296 ///296 ///297 /// @dev Owner can be changed only by current owner297 /// @dev Owner can be changed only by current owner298 /// @param newOwner new owner account298 /// @param newOwner new owner account299 /// @dev EVM selector for this function is: 0x13af4035,299 /// @dev EVM selector for this function is: 0x4f53e226,300 /// or in textual repr: setOwner(address)300 /// or in textual repr: changeCollectionOwner(address)301 function setOwner(address newOwner) public {301 function changeCollectionOwner(address newOwner) public {302 require(false, stub_error);302 require(false, stub_error);303 newOwner;303 newOwner;304 dummy = 0;304 dummy = 0;pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth55 owner,55 owner,56 CollectionMode::NFT,56 CollectionMode::NFT,57 |owner: T::CrossAccountId, data| {57 |owner: T::CrossAccountId, data| {58 <Pallet<T>>::init_collection(owner.clone(), owner, data, true)58 <Pallet<T>>::init_collection(owner.clone(), owner, data, Default::default())59 },59 },60 NonfungibleHandle::cast,60 NonfungibleHandle::cast,61 )61 )pallets/nonfungible/src/erc.rsdiffbeforeafterboth35use pallet_common::{35use pallet_common::{36 erc::{36 erc::{CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key},37 CommonEvmHandler, PrecompileResult, CollectionCall,38 static_property::{key, value as property_value},39 },40 CollectionHandle, CollectionPropertyPermissions,37 CollectionHandle, CollectionPropertyPermissions,41};38};42use pallet_evm::{account::CrossAccountId, PrecompileHandle};39use pallet_evm::{account::CrossAccountId, PrecompileHandle};43use pallet_evm_coder_substrate::call;40use pallet_evm_coder_substrate::call;44use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};41use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};45use alloc::string::ToString;464247use crate::{43use crate::{48 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,44 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,194}190}195191196#[derive(ToLog)]192#[derive(ToLog)]197pub enum ERC721MintableEvents {193pub enum ERC721UniqueMintableEvents {198 #[allow(dead_code)]194 #[allow(dead_code)]199 MintingFinished {},195 MintingFinished {},200}196}204#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f)]200#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f)]205impl<T: Config> NonfungibleHandle<T> {201impl<T: Config> NonfungibleHandle<T> {206 /// @notice A descriptive name for a collection of NFTs in this contract202 /// @notice A descriptive name for a collection of NFTs in this contract203 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`204 #[solidity(hide, rename_selector = "name")]207 fn name(&self) -> Result<string> {205 fn name_proxy(&self) -> Result<string> {208 Ok(decode_utf16(self.name.iter().copied())206 self.name()209 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))210 .collect::<string>())211 }207 }212208213 /// @notice An abbreviated name for NFTs in this contract209 /// @notice An abbreviated name for NFTs in this contract210 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`211 #[solidity(hide, rename_selector = "symbol")]214 fn symbol(&self) -> Result<string> {212 fn symbol_proxy(&self) -> Result<string> {215 Ok(string::from_utf8_lossy(&self.token_prefix).into())213 self.symbol()216 }214 }217215218 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.216 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.228 fn token_uri(&self, token_id: uint256) -> Result<string> {226 fn token_uri(&self, token_id: uint256) -> Result<string> {229 let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;227 let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;230228231 if let Ok(url) = get_token_property(self, token_id_u32, &key::url()) {229 match get_token_property(self, token_id_u32, &key::url()).as_deref() {232 if !url.is_empty() {230 Err(_) | Ok("") => (),233 return Ok(url);234 }235 } else if !is_erc721_metadata_compatible::<T>(self.id) {231 Ok(url) => {236 return Err("tokenURI not set".into());232 return Ok(url.into());237 }233 }234 };238235239 if let Some(base_uri) =236 let base_uri =240 pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())237 pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())241 {242 if !base_uri.is_empty() {238 .map(BoundedVec::into_inner)243 let base_uri = string::from_utf8(base_uri.into_inner()).map_err(|e| {239 .map(string::from_utf8)240 .transpose()241 .map_err(|e| {244 Error::Revert(alloc::format!(242 Error::Revert(alloc::format!(245 "Can not convert value \"baseURI\" to string with error \"{}\"",243 "Can not convert value \"baseURI\" to string with error \"{}\"",246 e244 e247 ))245 ))248 })?;246 })?;249 if let Ok(suffix) = get_token_property(self, token_id_u32, &key::suffix()) {247250 if !suffix.is_empty() {248 let base_uri = match base_uri.as_deref() {251 return Ok(base_uri + suffix.as_str());249 None | Some("") => {252 }253 }254255 return Ok(base_uri + token_id.to_string().as_str());256 }257 }258259 Ok("".into())250 return Ok("".into());251 }252 Some(base_uri) => base_uri.into(),253 };254255 Ok(256 match get_token_property(self, token_id_u32, &key::suffix()).as_deref() {257 Err(_) | Ok("") => base_uri,258 Ok(suffix) => base_uri + suffix,259 },260 )260 }261 }261}262}262263427}428}428429429/// @title ERC721 minting logic.430/// @title ERC721 minting logic.430#[solidity_interface(name = ERC721Mintable, events(ERC721MintableEvents))]431#[solidity_interface(name = ERC721UniqueMintable, events(ERC721UniqueMintableEvents))]431impl<T: Config> NonfungibleHandle<T> {432impl<T: Config> NonfungibleHandle<T> {432 fn minting_finished(&self) -> Result<bool> {433 fn minting_finished(&self) -> Result<bool> {433 Ok(false)434 Ok(false)434 }435 }436437 /// @notice Function to mint token.438 /// @param to The new owner439 /// @return uint256 The id of the newly minted token440 #[weight(<SelfWeightOf<T>>::create_item())]441 fn mint(&mut self, caller: caller, to: address) -> Result<uint256> {442 let token_id: uint256 = <TokensMinted<T>>::get(self.id)443 .checked_add(1)444 .ok_or("item id overflow")?445 .into();446 self.mint_check_id(caller, to, token_id)?;447 Ok(token_id)448 }435449436 /// @notice Function to mint token.450 /// @notice Function to mint token.437 /// @dev `tokenId` should be obtained with `nextTokenId` method,451 /// @dev `tokenId` should be obtained with `nextTokenId` method,438 /// unlike standard, you can't specify it manually452 /// unlike standard, you can't specify it manually439 /// @param to The new owner453 /// @param to The new owner440 /// @param tokenId ID of the minted NFT454 /// @param tokenId ID of the minted NFT455 #[solidity(hide, rename_selector = "mint")]441 #[weight(<SelfWeightOf<T>>::create_item())]456 #[weight(<SelfWeightOf<T>>::create_item())]442 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {457 fn mint_check_id(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {443 let caller = T::CrossAccountId::from_eth(caller);458 let caller = T::CrossAccountId::from_eth(caller);444 let to = T::CrossAccountId::from_eth(to);459 let to = T::CrossAccountId::from_eth(to);445 let token_id: u32 = token_id.try_into()?;460 let token_id: u32 = token_id.try_into()?;469 Ok(true)484 Ok(true)470 }485 }486487 /// @notice Function to mint token with the given tokenUri.488 /// @param to The new owner489 /// @param tokenUri Token URI that would be stored in the NFT properties490 /// @return uint256 The id of the newly minted token491 #[solidity(rename_selector = "mintWithTokenURI")]492 #[weight(<SelfWeightOf<T>>::create_item())]493 fn mint_with_token_uri(494 &mut self,495 caller: caller,496 to: address,497 token_uri: string,498 ) -> Result<uint256> {499 let token_id: uint256 = <TokensMinted<T>>::get(self.id)500 .checked_add(1)501 .ok_or("item id overflow")?502 .into();503 self.mint_with_token_uri_check_id(caller, to, token_id, token_uri)?;504 Ok(token_id)505 }471506472 /// @notice Function to mint token with the given tokenUri.507 /// @notice Function to mint token with the given tokenUri.473 /// @dev `tokenId` should be obtained with `nextTokenId` method,508 /// @dev `tokenId` should be obtained with `nextTokenId` method,474 /// unlike standard, you can't specify it manually509 /// unlike standard, you can't specify it manually475 /// @param to The new owner510 /// @param to The new owner476 /// @param tokenId ID of the minted NFT511 /// @param tokenId ID of the minted NFT477 /// @param tokenUri Token URI that would be stored in the NFT properties512 /// @param tokenUri Token URI that would be stored in the NFT properties478 #[solidity(rename_selector = "mintWithTokenURI")]513 #[solidity(hide, rename_selector = "mintWithTokenURI")]479 #[weight(<SelfWeightOf<T>>::create_item())]514 #[weight(<SelfWeightOf<T>>::create_item())]480 fn mint_with_token_uri(515 fn mint_with_token_uri_check_id(481 &mut self,516 &mut self,482 caller: caller,517 caller: caller,483 to: address,518 to: address,550 Err("Property tokenURI not found".into())585 Err("Property tokenURI not found".into())551}586}552553fn is_erc721_metadata_compatible<T: Config>(collection_id: CollectionId) -> bool {554 if let Some(shema_name) =555 pallet_common::Pallet::<T>::get_collection_property(collection_id, &key::schema_name())556 {557 let shema_name = shema_name.into_inner();558 shema_name == property_value::ERC721_METADATA559 } else {560 false561 }562}563587564fn get_token_permission<T: Config>(588fn get_token_permission<T: Config>(565 collection_id: CollectionId,589 collection_id: CollectionId,577 Ok(a)601 Ok(a)578}602}579580fn has_token_permission<T: Config>(collection_id: CollectionId, key: &PropertyKey) -> bool {581 if let Ok(token_property_permissions) =582 CollectionPropertyPermissions::<T>::try_get(collection_id)583 {584 return token_property_permissions.contains_key(key);585 }586587 false588}589603590/// @title Unique extensions for ERC721.604/// @title Unique extensions for ERC721.591#[solidity_interface(name = ERC721UniqueExtensions)]605#[solidity_interface(name = ERC721UniqueExtensions)]592impl<T: Config> NonfungibleHandle<T> {606impl<T: Config> NonfungibleHandle<T> {607 /// @notice A descriptive name for a collection of NFTs in this contract608 fn name(&self) -> Result<string> {609 Ok(decode_utf16(self.name.iter().copied())610 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))611 .collect::<string>())612 }613614 /// @notice An abbreviated name for NFTs in this contract615 fn symbol(&self) -> Result<string> {616 Ok(string::from_utf8_lossy(&self.token_prefix).into())617 }618593 /// @notice Transfer ownership of an NFT619 /// @notice Transfer ownership of an NFT594 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`620 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`642 /// should be obtained with `nextTokenId` method668 /// should be obtained with `nextTokenId` method643 /// @param to The new owner669 /// @param to The new owner644 /// @param tokenIds IDs of the minted NFTs670 /// @param tokenIds IDs of the minted NFTs671 // #[solidity(hide)]645 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]672 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]646 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {673 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {647 let caller = T::CrossAccountId::from_eth(caller);674 let caller = T::CrossAccountId::from_eth(caller);678 /// numbers and first number should be obtained with `nextTokenId` method705 /// numbers and first number should be obtained with `nextTokenId` method679 /// @param to The new owner706 /// @param to The new owner680 /// @param tokens array of pairs of token ID and token URI for minted tokens707 /// @param tokens array of pairs of token ID and token URI for minted tokens681 #[solidity(rename_selector = "mintBulkWithTokenURI")]708 #[solidity(/*hide,*/ rename_selector = "mintBulkWithTokenURI")]682 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]709 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]683 fn mint_bulk_with_token_uri(710 fn mint_bulk_with_token_uri(684 &mut self,711 &mut self,731 name = UniqueNFT,758 name = UniqueNFT,732 is(759 is(733 ERC721,760 ERC721,734 ERC721Metadata,735 ERC721Enumerable,761 ERC721Enumerable,736 ERC721UniqueExtensions,762 ERC721UniqueExtensions,737 ERC721Mintable,763 ERC721UniqueMintable,738 ERC721Burnable,764 ERC721Burnable,765 ERC721Metadata(if(this.flags.erc721metadata)),739 Collection(via(common_mut returns CollectionHandle<T>)),766 Collection(via(common_mut returns CollectionHandle<T>)),740 TokenProperties,767 TokenProperties,741 )768 )pallets/nonfungible/src/lib.rsdiffbeforeafterboth407 owner: T::CrossAccountId,408 owner: T::CrossAccountId,408 payer: T::CrossAccountId,409 payer: T::CrossAccountId,409 data: CreateCollectionData<T::AccountId>,410 data: CreateCollectionData<T::AccountId>,410 is_external: bool,411 flags: CollectionFlags,411 ) -> Result<CollectionId, DispatchError> {412 ) -> Result<CollectionId, DispatchError> {412 <PalletCommon<T>>::init_collection(413 <PalletCommon<T>>::init_collection(owner, payer, data, flags)413 owner,414 payer,415 data,416 CollectionFlags {417 external: is_external,418 ..Default::default()419 },420 )421 }414 }422415pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth91}91}929293/// @title A contract that allows you to work with collections.93/// @title A contract that allows you to work with collections.94/// @dev the ERC-165 identifier for this interface is 0x3e1e808394/// @dev the ERC-165 identifier for this interface is 0x62e2229095contract Collection is Dummy, ERC165 {95contract Collection is Dummy, ERC165 {96 /// Set collection property.96 /// Set collection property.97 ///97 ///369 ///369 ///370 /// @dev Owner can be changed only by current owner370 /// @dev Owner can be changed only by current owner371 /// @param newOwner new owner account371 /// @param newOwner new owner account372 /// @dev EVM selector for this function is: 0x13af4035,372 /// @dev EVM selector for this function is: 0x4f53e226,373 /// or in textual repr: setOwner(address)373 /// or in textual repr: changeCollectionOwner(address)374 function setOwner(address newOwner) public {374 function changeCollectionOwner(address newOwner) public {375 require(false, stub_error);375 require(false, stub_error);376 newOwner;376 newOwner;377 dummy = 0;377 dummy = 0;384 uint256 field_1;384 uint256 field_1;385}385}386387/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension388/// @dev See https://eips.ethereum.org/EIPS/eip-721389/// @dev the ERC-165 identifier for this interface is 0x5b5e139f390contract ERC721Metadata is Dummy, ERC165 {391 // /// @notice A descriptive name for a collection of NFTs in this contract392 // /// @dev real implementation of this function lies in `ERC721UniqueExtensions`393 // /// @dev EVM selector for this function is: 0x06fdde03,394 // /// or in textual repr: name()395 // function name() public view returns (string memory) {396 // require(false, stub_error);397 // dummy;398 // return "";399 // }400401 // /// @notice An abbreviated name for NFTs in this contract402 // /// @dev real implementation of this function lies in `ERC721UniqueExtensions`403 // /// @dev EVM selector for this function is: 0x95d89b41,404 // /// or in textual repr: symbol()405 // function symbol() public view returns (string memory) {406 // require(false, stub_error);407 // dummy;408 // return "";409 // }410411 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.412 ///413 /// @dev If the token has a `url` property and it is not empty, it is returned.414 /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.415 /// If the collection property `baseURI` is empty or absent, return "" (empty string)416 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix417 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).418 ///419 /// @return token's const_metadata420 /// @dev EVM selector for this function is: 0xc87b56dd,421 /// or in textual repr: tokenURI(uint256)422 function tokenURI(uint256 tokenId) public view returns (string memory) {423 require(false, stub_error);424 tokenId;425 dummy;426 return "";427 }428}386429387/// @title ERC721 Token that can be irreversibly burned (destroyed).430/// @title ERC721 Token that can be irreversibly burned (destroyed).388/// @dev the ERC-165 identifier for this interface is 0x42966c68431/// @dev the ERC-165 identifier for this interface is 0x42966c68401}444}402445403/// @dev inlined interface446/// @dev inlined interface404contract ERC721MintableEvents {447contract ERC721UniqueMintableEvents {405 event MintingFinished();448 event MintingFinished();406}449}407450408/// @title ERC721 minting logic.451/// @title ERC721 minting logic.409/// @dev the ERC-165 identifier for this interface is 0x68ccfe89452/// @dev the ERC-165 identifier for this interface is 0x476ff149410contract ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {453contract ERC721UniqueMintable is Dummy, ERC165, ERC721UniqueMintableEvents {411 /// @dev EVM selector for this function is: 0x05d2035b,454 /// @dev EVM selector for this function is: 0x05d2035b,412 /// or in textual repr: mintingFinished()455 /// or in textual repr: mintingFinished()413 function mintingFinished() public view returns (bool) {456 function mintingFinished() public view returns (bool) {417 }460 }418461419 /// @notice Function to mint token.462 /// @notice Function to mint token.420 /// @dev `tokenId` should be obtained with `nextTokenId` method,421 /// unlike standard, you can't specify it manually422 /// @param to The new owner463 /// @param to The new owner423 /// @param tokenId ID of the minted NFT464 /// @return uint256 The id of the newly minted token424 /// @dev EVM selector for this function is: 0x40c10f19,465 /// @dev EVM selector for this function is: 0x6a627842,425 /// or in textual repr: mint(address,uint256)466 /// or in textual repr: mint(address)426 function mint(address to, uint256 tokenId) public returns (bool) {467 function mint(address to) public returns (uint256) {427 require(false, stub_error);468 require(false, stub_error);428 to;469 to;429 tokenId;430 dummy = 0;470 dummy = 0;431 return false;471 return 0;432 }472 }473474 // /// @notice Function to mint token.475 // /// @dev `tokenId` should be obtained with `nextTokenId` method,476 // /// unlike standard, you can't specify it manually477 // /// @param to The new owner478 // /// @param tokenId ID of the minted NFT479 // /// @dev EVM selector for this function is: 0x40c10f19,480 // /// or in textual repr: mint(address,uint256)481 // function mint(address to, uint256 tokenId) public returns (bool) {482 // require(false, stub_error);483 // to;484 // tokenId;485 // dummy = 0;486 // return false;487 // }433488434 /// @notice Function to mint token with the given tokenUri.489 /// @notice Function to mint token with the given tokenUri.435 /// @dev `tokenId` should be obtained with `nextTokenId` method,436 /// unlike standard, you can't specify it manually437 /// @param to The new owner490 /// @param to The new owner438 /// @param tokenId ID of the minted NFT439 /// @param tokenUri Token URI that would be stored in the NFT properties491 /// @param tokenUri Token URI that would be stored in the NFT properties492 /// @return uint256 The id of the newly minted token440 /// @dev EVM selector for this function is: 0x50bb4e7f,493 /// @dev EVM selector for this function is: 0x45c17782,441 /// or in textual repr: mintWithTokenURI(address,uint256,string)494 /// or in textual repr: mintWithTokenURI(address,string)442 function mintWithTokenURI(495 function mintWithTokenURI(address to, string memory tokenUri) public returns (uint256) {443 address to,444 uint256 tokenId,445 string memory tokenUri446 ) public returns (bool) {447 require(false, stub_error);496 require(false, stub_error);448 to;497 to;449 tokenId;450 tokenUri;498 tokenUri;451 dummy = 0;499 dummy = 0;452 return false;500 return 0;453 }501 }502503 // /// @notice Function to mint token with the given tokenUri.504 // /// @dev `tokenId` should be obtained with `nextTokenId` method,505 // /// unlike standard, you can't specify it manually506 // /// @param to The new owner507 // /// @param tokenId ID of the minted NFT508 // /// @param tokenUri Token URI that would be stored in the NFT properties509 // /// @dev EVM selector for this function is: 0x50bb4e7f,510 // /// or in textual repr: mintWithTokenURI(address,uint256,string)511 // function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) public returns (bool) {512 // require(false, stub_error);513 // to;514 // tokenId;515 // tokenUri;516 // dummy = 0;517 // return false;518 // }454519455 /// @dev Not implemented520 /// @dev Not implemented456 /// @dev EVM selector for this function is: 0x7d64bcb4,521 /// @dev EVM selector for this function is: 0x7d64bcb4,463}528}464529465/// @title Unique extensions for ERC721.530/// @title Unique extensions for ERC721.466/// @dev the ERC-165 identifier for this interface is 0xd74d154f531/// @dev the ERC-165 identifier for this interface is 0x4468500d467contract ERC721UniqueExtensions is Dummy, ERC165 {532contract ERC721UniqueExtensions is Dummy, ERC165 {533 /// @notice A descriptive name for a collection of NFTs in this contract534 /// @dev EVM selector for this function is: 0x06fdde03,535 /// or in textual repr: name()536 function name() public view returns (string memory) {537 require(false, stub_error);538 dummy;539 return "";540 }541542 /// @notice An abbreviated name for NFTs in this contract543 /// @dev EVM selector for this function is: 0x95d89b41,544 /// or in textual repr: symbol()545 function symbol() public view returns (string memory) {546 require(false, stub_error);547 dummy;548 return "";549 }550468 /// @notice Transfer ownership of an NFT551 /// @notice Transfer ownership of an NFT469 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`552 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`525 /// @param tokens array of pairs of token ID and token URI for minted tokens608 /// @param tokens array of pairs of token ID and token URI for minted tokens526 /// @dev EVM selector for this function is: 0x36543006,609 /// @dev EVM selector for this function is: 0x36543006,527 /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])610 /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])528 function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) public returns (bool) {611 function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) public returns (bool) {529 require(false, stub_error);612 require(false, stub_error);530 to;613 to;531 tokens;614 tokens;535}618}536619537/// @dev anonymous struct620/// @dev anonymous struct538struct Tuple8 {621struct Tuple6 {539 uint256 field_0;622 uint256 field_0;540 string field_1;623 string field_1;541}624}580 }663 }581}664}582583/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension584/// @dev See https://eips.ethereum.org/EIPS/eip-721585/// @dev the ERC-165 identifier for this interface is 0x5b5e139f586contract ERC721Metadata is Dummy, ERC165 {587 /// @notice A descriptive name for a collection of NFTs in this contract588 /// @dev EVM selector for this function is: 0x06fdde03,589 /// or in textual repr: name()590 function name() public view returns (string memory) {591 require(false, stub_error);592 dummy;593 return "";594 }595596 /// @notice An abbreviated name for NFTs in this contract597 /// @dev EVM selector for this function is: 0x95d89b41,598 /// or in textual repr: symbol()599 function symbol() public view returns (string memory) {600 require(false, stub_error);601 dummy;602 return "";603 }604605 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.606 ///607 /// @dev If the token has a `url` property and it is not empty, it is returned.608 /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.609 /// If the collection property `baseURI` is empty or absent, return "" (empty string)610 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix611 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).612 ///613 /// @return token's const_metadata614 /// @dev EVM selector for this function is: 0xc87b56dd,615 /// or in textual repr: tokenURI(uint256)616 function tokenURI(uint256 tokenId) public view returns (string memory) {617 require(false, stub_error);618 tokenId;619 dummy;620 return "";621 }622}623665624/// @dev inlined interface666/// @dev inlined interface625contract ERC721Events {667contract ERC721Events {766 Dummy,808 Dummy,767 ERC165,809 ERC165,768 ERC721,810 ERC721,769 ERC721Metadata,770 ERC721Enumerable,811 ERC721Enumerable,771 ERC721UniqueExtensions,812 ERC721UniqueExtensions,772 ERC721Mintable,813 ERC721UniqueMintable,773 ERC721Burnable,814 ERC721Burnable,815 ERC721Metadata,774 Collection,816 Collection,775 TokenProperties817 TokenProperties776{}818{}pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth1452 sender.clone(),1453 sender,1454 data,1455 up_data_structs::CollectionFlags {1456 external: true,1457 ..Default::default()1458 },1459 );145214601453 if let Err(DispatchError::Arithmetic(_)) = &collection_id {1461 if let Err(DispatchError::Arithmetic(_)) = &collection_id {pallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth254 cross_sender.clone(),254 cross_sender.clone(),255 cross_sender.clone(),255 cross_sender.clone(),256 data,256 data,257 up_data_structs::CollectionFlags {257 true,258 external: true,259 ..Default::default()260 },258 );261 );259262260 if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {263 if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {pallets/refungible/src/erc.rsdiffbeforeafterboth212122extern crate alloc;22extern crate alloc;232324use alloc::string::ToString;25use core::{24use core::{26 char::{REPLACEMENT_CHARACTER, decode_utf16},25 char::{REPLACEMENT_CHARACTER, decode_utf16},27 convert::TryInto,26 convert::TryInto,28};27};29use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};28use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};30use frame_support::BoundedBTreeMap;29use frame_support::{BoundedBTreeMap, BoundedVec};31use pallet_common::{30use pallet_common::{32 CollectionHandle, CollectionPropertyPermissions,31 CollectionHandle, CollectionPropertyPermissions,33 erc::{32 erc::{CommonEvmHandler, CollectionCall, static_property::key},34 CommonEvmHandler, CollectionCall,35 static_property::{key, value as property_value},36 },37};33};38use pallet_evm::{account::CrossAccountId, PrecompileHandle};34use pallet_evm::{account::CrossAccountId, PrecompileHandle};191}187}192188193#[derive(ToLog)]189#[derive(ToLog)]194pub enum ERC721MintableEvents {190pub enum ERC721UniqueMintableEvents {195 /// @dev Not supported191 /// @dev Not supported196 #[allow(dead_code)]192 #[allow(dead_code)]197 MintingFinished {},193 MintingFinished {},198}194}199195200#[solidity_interface(name = ERC721Metadata)]196#[solidity_interface(name = ERC721Metadata)]201impl<T: Config> RefungibleHandle<T> {197impl<T: Config> RefungibleHandle<T> {202 /// @notice A descriptive name for a collection of RFTs in this contract198 /// @notice A descriptive name for a collection of NFTs in this contract199 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`200 #[solidity(hide, rename_selector = "name")]203 fn name(&self) -> Result<string> {201 fn name_proxy(&self) -> Result<string> {204 Ok(decode_utf16(self.name.iter().copied())202 self.name()205 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))206 .collect::<string>())207 }203 }208204209 /// @notice An abbreviated name for RFTs in this contract205 /// @notice An abbreviated name for NFTs in this contract206 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`207 #[solidity(hide, rename_selector = "symbol")]210 fn symbol(&self) -> Result<string> {208 fn symbol_proxy(&self) -> Result<string> {211 Ok(string::from_utf8_lossy(&self.token_prefix).into())209 self.symbol()212 }210 }213211214 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.212 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.224 fn token_uri(&self, token_id: uint256) -> Result<string> {222 fn token_uri(&self, token_id: uint256) -> Result<string> {225 let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;223 let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;226224227 if let Ok(url) = get_token_property(self, token_id_u32, &key::url()) {225 match get_token_property(self, token_id_u32, &key::url()).as_deref() {228 if !url.is_empty() {226 Err(_) | Ok("") => (),229 return Ok(url);230 }231 } else if !is_erc721_metadata_compatible::<T>(self.id) {227 Ok(url) => {232 return Err("tokenURI not set".into());228 return Ok(url.into());233 }229 }230 };234231235 if let Some(base_uri) =232 let base_uri =236 pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())233 pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())237 {238 if !base_uri.is_empty() {234 .map(BoundedVec::into_inner)239 let base_uri = string::from_utf8(base_uri.into_inner()).map_err(|e| {235 .map(string::from_utf8)236 .transpose()237 .map_err(|e| {240 Error::Revert(alloc::format!(238 Error::Revert(alloc::format!(241 "Can not convert value \"baseURI\" to string with error \"{}\"",239 "Can not convert value \"baseURI\" to string with error \"{}\"",242 e240 e243 ))241 ))244 })?;242 })?;245 if let Ok(suffix) = get_token_property(self, token_id_u32, &key::suffix()) {243246 if !suffix.is_empty() {244 let base_uri = match base_uri.as_deref() {247 return Ok(base_uri + suffix.as_str());245 None | Some("") => {248 }249 }250251 return Ok(base_uri + token_id.to_string().as_str());252 }253 }254255 Ok("".into())246 return Ok("".into());247 }248 Some(base_uri) => base_uri.into(),249 };250251 Ok(252 match get_token_property(self, token_id_u32, &key::suffix()).as_deref() {253 Err(_) | Ok("") => base_uri,254 Ok(suffix) => base_uri + suffix,255 },256 )256 }257 }257}258}258259448}449}449450450/// @title ERC721 minting logic.451/// @title ERC721 minting logic.451#[solidity_interface(name = ERC721Mintable, events(ERC721MintableEvents))]452#[solidity_interface(name = ERC721UniqueMintable, events(ERC721UniqueMintableEvents))]452impl<T: Config> RefungibleHandle<T> {453impl<T: Config> RefungibleHandle<T> {453 fn minting_finished(&self) -> Result<bool> {454 fn minting_finished(&self) -> Result<bool> {454 Ok(false)455 Ok(false)455 }456 }457458 /// @notice Function to mint token.459 /// @param to The new owner460 /// @return uint256 The id of the newly minted token461 #[weight(<SelfWeightOf<T>>::create_item())]462 fn mint(&mut self, caller: caller, to: address) -> Result<uint256> {463 let token_id: uint256 = <TokensMinted<T>>::get(self.id)464 .checked_add(1)465 .ok_or("item id overflow")?466 .into();467 self.mint_check_id(caller, to, token_id)?;468 Ok(token_id)469 }456470457 /// @notice Function to mint token.471 /// @notice Function to mint token.458 /// @dev `tokenId` should be obtained with `nextTokenId` method,472 /// @dev `tokenId` should be obtained with `nextTokenId` method,459 /// unlike standard, you can't specify it manually473 /// unlike standard, you can't specify it manually460 /// @param to The new owner474 /// @param to The new owner461 /// @param tokenId ID of the minted RFT475 /// @param tokenId ID of the minted RFT476 #[solidity(hide, rename_selector = "mint")]462 #[weight(<SelfWeightOf<T>>::create_item())]477 #[weight(<SelfWeightOf<T>>::create_item())]463 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {478 fn mint_check_id(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {464 let caller = T::CrossAccountId::from_eth(caller);479 let caller = T::CrossAccountId::from_eth(caller);465 let to = T::CrossAccountId::from_eth(to);480 let to = T::CrossAccountId::from_eth(to);466 let token_id: u32 = token_id.try_into()?;481 let token_id: u32 = token_id.try_into()?;495 Ok(true)510 Ok(true)496 }511 }512513 /// @notice Function to mint token with the given tokenUri.514 /// @param to The new owner515 /// @param tokenUri Token URI that would be stored in the NFT properties516 /// @return uint256 The id of the newly minted token517 #[solidity(rename_selector = "mintWithTokenURI")]518 #[weight(<SelfWeightOf<T>>::create_item())]519 fn mint_with_token_uri(520 &mut self,521 caller: caller,522 to: address,523 token_uri: string,524 ) -> Result<uint256> {525 let token_id: uint256 = <TokensMinted<T>>::get(self.id)526 .checked_add(1)527 .ok_or("item id overflow")?528 .into();529 self.mint_with_token_uri_check_id(caller, to, token_id, token_uri)?;530 Ok(token_id)531 }497532498 /// @notice Function to mint token with the given tokenUri.533 /// @notice Function to mint token with the given tokenUri.499 /// @dev `tokenId` should be obtained with `nextTokenId` method,534 /// @dev `tokenId` should be obtained with `nextTokenId` method,500 /// unlike standard, you can't specify it manually535 /// unlike standard, you can't specify it manually501 /// @param to The new owner536 /// @param to The new owner502 /// @param tokenId ID of the minted RFT537 /// @param tokenId ID of the minted RFT503 /// @param tokenUri Token URI that would be stored in the RFT properties538 /// @param tokenUri Token URI that would be stored in the RFT properties504 #[solidity(rename_selector = "mintWithTokenURI")]539 #[solidity(hide, rename_selector = "mintWithTokenURI")]505 #[weight(<SelfWeightOf<T>>::create_item())]540 #[weight(<SelfWeightOf<T>>::create_item())]506 fn mint_with_token_uri(541 fn mint_with_token_uri_check_id(507 &mut self,542 &mut self,508 caller: caller,543 caller: caller,509 to: address,544 to: address,578 Err("Property tokenURI not found".into())613 Err("Property tokenURI not found".into())579}614}580581fn is_erc721_metadata_compatible<T: Config>(collection_id: CollectionId) -> bool {582 if let Some(shema_name) =583 pallet_common::Pallet::<T>::get_collection_property(collection_id, &key::schema_name())584 {585 let shema_name = shema_name.into_inner();586 shema_name == property_value::ERC721_METADATA587 } else {588 false589 }590}591615592fn get_token_permission<T: Config>(616fn get_token_permission<T: Config>(593 collection_id: CollectionId,617 collection_id: CollectionId,608/// @title Unique extensions for ERC721.632/// @title Unique extensions for ERC721.609#[solidity_interface(name = ERC721UniqueExtensions)]633#[solidity_interface(name = ERC721UniqueExtensions)]610impl<T: Config> RefungibleHandle<T> {634impl<T: Config> RefungibleHandle<T> {635 /// @notice A descriptive name for a collection of NFTs in this contract636 fn name(&self) -> Result<string> {637 Ok(decode_utf16(self.name.iter().copied())638 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))639 .collect::<string>())640 }641642 /// @notice An abbreviated name for NFTs in this contract643 fn symbol(&self) -> Result<string> {644 Ok(string::from_utf8_lossy(&self.token_prefix).into())645 }646611 /// @notice Transfer ownership of an RFT647 /// @notice Transfer ownership of an RFT612 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`648 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`669 /// should be obtained with `nextTokenId` method705 /// should be obtained with `nextTokenId` method670 /// @param to The new owner706 /// @param to The new owner671 /// @param tokenIds IDs of the minted RFTs707 /// @param tokenIds IDs of the minted RFTs708 // #[solidity(hide)]672 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]709 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]673 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {710 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {674 let caller = T::CrossAccountId::from_eth(caller);711 let caller = T::CrossAccountId::from_eth(caller);711 /// numbers and first number should be obtained with `nextTokenId` method748 /// numbers and first number should be obtained with `nextTokenId` method712 /// @param to The new owner749 /// @param to The new owner713 /// @param tokens array of pairs of token ID and token URI for minted tokens750 /// @param tokens array of pairs of token ID and token URI for minted tokens714 #[solidity(rename_selector = "mintBulkWithTokenURI")]751 #[solidity(/*hide,*/ rename_selector = "mintBulkWithTokenURI")]715 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]752 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]716 fn mint_bulk_with_token_uri(753 fn mint_bulk_with_token_uri(717 &mut self,754 &mut self,780 name = UniqueRefungible,817 name = UniqueRefungible,781 is(818 is(782 ERC721,819 ERC721,783 ERC721Metadata,784 ERC721Enumerable,820 ERC721Enumerable,785 ERC721UniqueExtensions,821 ERC721UniqueExtensions,786 ERC721Mintable,822 ERC721UniqueMintable,787 ERC721Burnable,823 ERC721Burnable,824 ERC721Metadata(if(this.flags.erc721metadata)),788 Collection(via(common_mut returns CollectionHandle<T>)),825 Collection(via(common_mut returns CollectionHandle<T>)),789 TokenProperties,826 TokenProperties,790 )827 )pallets/refungible/src/lib.rsdiffbeforeafterboth929293use codec::{Encode, Decode, MaxEncodedLen};93use codec::{Encode, Decode, MaxEncodedLen};94use core::ops::Deref;94use core::ops::Deref;95use derivative::Derivative;95use evm_coder::ToLog;96use evm_coder::ToLog;96use frame_support::{97use frame_support::{97 BoundedVec, ensure, fail, storage::with_transaction, transactional, pallet_prelude::ConstU32,98 BoundedBTreeMap, BoundedVec, ensure, fail, storage::with_transaction, transactional,99 pallet_prelude::ConstU32,98};100};99use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};101use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};113 MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyPermission,115 MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyPermission,114 PropertyScope, PropertyValue, TokenId, TrySetProperty,116 PropertyScope, PropertyValue, TokenId, TrySetProperty,115};117};116use frame_support::BoundedBTreeMap;117use derivative::Derivative;118118119pub use pallet::*;119pub use pallet::*;120#[cfg(feature = "runtime-benchmarks")]120#[cfg(feature = "runtime-benchmarks")]371 owner: T::CrossAccountId,371 owner: T::CrossAccountId,372 payer: T::CrossAccountId,372 payer: T::CrossAccountId,373 data: CreateCollectionData<T::AccountId>,373 data: CreateCollectionData<T::AccountId>,374 flags: CollectionFlags,374 ) -> Result<CollectionId, DispatchError> {375 ) -> Result<CollectionId, DispatchError> {375 <PalletCommon<T>>::init_collection(owner, payer, data, CollectionFlags::default())376 <PalletCommon<T>>::init_collection(owner, payer, data, flags)376 }377 }377378378 /// Destroy RFT collection379 /// Destroy RFT collectionpallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth91}91}929293/// @title A contract that allows you to work with collections.93/// @title A contract that allows you to work with collections.94/// @dev the ERC-165 identifier for this interface is 0x3e1e808394/// @dev the ERC-165 identifier for this interface is 0x62e2229095contract Collection is Dummy, ERC165 {95contract Collection is Dummy, ERC165 {96 /// Set collection property.96 /// Set collection property.97 ///97 ///369 ///369 ///370 /// @dev Owner can be changed only by current owner370 /// @dev Owner can be changed only by current owner371 /// @param newOwner new owner account371 /// @param newOwner new owner account372 /// @dev EVM selector for this function is: 0x13af4035,372 /// @dev EVM selector for this function is: 0x4f53e226,373 /// or in textual repr: setOwner(address)373 /// or in textual repr: changeCollectionOwner(address)374 function setOwner(address newOwner) public {374 function changeCollectionOwner(address newOwner) public {375 require(false, stub_error);375 require(false, stub_error);376 newOwner;376 newOwner;377 dummy = 0;377 dummy = 0;384 uint256 field_1;384 uint256 field_1;385}385}386387/// @dev the ERC-165 identifier for this interface is 0x5b5e139f388contract ERC721Metadata is Dummy, ERC165 {389 // /// @notice A descriptive name for a collection of NFTs in this contract390 // /// @dev real implementation of this function lies in `ERC721UniqueExtensions`391 // /// @dev EVM selector for this function is: 0x06fdde03,392 // /// or in textual repr: name()393 // function name() public view returns (string memory) {394 // require(false, stub_error);395 // dummy;396 // return "";397 // }398399 // /// @notice An abbreviated name for NFTs in this contract400 // /// @dev real implementation of this function lies in `ERC721UniqueExtensions`401 // /// @dev EVM selector for this function is: 0x95d89b41,402 // /// or in textual repr: symbol()403 // function symbol() public view returns (string memory) {404 // require(false, stub_error);405 // dummy;406 // return "";407 // }408409 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.410 ///411 /// @dev If the token has a `url` property and it is not empty, it is returned.412 /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.413 /// If the collection property `baseURI` is empty or absent, return "" (empty string)414 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix415 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).416 ///417 /// @return token's const_metadata418 /// @dev EVM selector for this function is: 0xc87b56dd,419 /// or in textual repr: tokenURI(uint256)420 function tokenURI(uint256 tokenId) public view returns (string memory) {421 require(false, stub_error);422 tokenId;423 dummy;424 return "";425 }426}386427387/// @title ERC721 Token that can be irreversibly burned (destroyed).428/// @title ERC721 Token that can be irreversibly burned (destroyed).388/// @dev the ERC-165 identifier for this interface is 0x42966c68429/// @dev the ERC-165 identifier for this interface is 0x42966c68401}442}402443403/// @dev inlined interface444/// @dev inlined interface404contract ERC721MintableEvents {445contract ERC721UniqueMintableEvents {405 event MintingFinished();446 event MintingFinished();406}447}407448408/// @title ERC721 minting logic.449/// @title ERC721 minting logic.409/// @dev the ERC-165 identifier for this interface is 0x68ccfe89450/// @dev the ERC-165 identifier for this interface is 0x476ff149410contract ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {451contract ERC721UniqueMintable is Dummy, ERC165, ERC721UniqueMintableEvents {411 /// @dev EVM selector for this function is: 0x05d2035b,452 /// @dev EVM selector for this function is: 0x05d2035b,412 /// or in textual repr: mintingFinished()453 /// or in textual repr: mintingFinished()413 function mintingFinished() public view returns (bool) {454 function mintingFinished() public view returns (bool) {417 }458 }418459419 /// @notice Function to mint token.460 /// @notice Function to mint token.420 /// @dev `tokenId` should be obtained with `nextTokenId` method,421 /// unlike standard, you can't specify it manually422 /// @param to The new owner461 /// @param to The new owner423 /// @param tokenId ID of the minted RFT462 /// @return uint256 The id of the newly minted token424 /// @dev EVM selector for this function is: 0x40c10f19,463 /// @dev EVM selector for this function is: 0x6a627842,425 /// or in textual repr: mint(address,uint256)464 /// or in textual repr: mint(address)426 function mint(address to, uint256 tokenId) public returns (bool) {465 function mint(address to) public returns (uint256) {427 require(false, stub_error);466 require(false, stub_error);428 to;467 to;429 tokenId;430 dummy = 0;468 dummy = 0;431 return false;469 return 0;432 }470 }471472 // /// @notice Function to mint token.473 // /// @dev `tokenId` should be obtained with `nextTokenId` method,474 // /// unlike standard, you can't specify it manually475 // /// @param to The new owner476 // /// @param tokenId ID of the minted RFT477 // /// @dev EVM selector for this function is: 0x40c10f19,478 // /// or in textual repr: mint(address,uint256)479 // function mint(address to, uint256 tokenId) public returns (bool) {480 // require(false, stub_error);481 // to;482 // tokenId;483 // dummy = 0;484 // return false;485 // }433486434 /// @notice Function to mint token with the given tokenUri.487 /// @notice Function to mint token with the given tokenUri.435 /// @dev `tokenId` should be obtained with `nextTokenId` method,436 /// unlike standard, you can't specify it manually437 /// @param to The new owner488 /// @param to The new owner438 /// @param tokenId ID of the minted RFT439 /// @param tokenUri Token URI that would be stored in the RFT properties489 /// @param tokenUri Token URI that would be stored in the NFT properties490 /// @return uint256 The id of the newly minted token440 /// @dev EVM selector for this function is: 0x50bb4e7f,491 /// @dev EVM selector for this function is: 0x45c17782,441 /// or in textual repr: mintWithTokenURI(address,uint256,string)492 /// or in textual repr: mintWithTokenURI(address,string)442 function mintWithTokenURI(493 function mintWithTokenURI(address to, string memory tokenUri) public returns (uint256) {443 address to,444 uint256 tokenId,445 string memory tokenUri446 ) public returns (bool) {447 require(false, stub_error);494 require(false, stub_error);448 to;495 to;449 tokenId;450 tokenUri;496 tokenUri;451 dummy = 0;497 dummy = 0;452 return false;498 return 0;453 }499 }500501 // /// @notice Function to mint token with the given tokenUri.502 // /// @dev `tokenId` should be obtained with `nextTokenId` method,503 // /// unlike standard, you can't specify it manually504 // /// @param to The new owner505 // /// @param tokenId ID of the minted RFT506 // /// @param tokenUri Token URI that would be stored in the RFT properties507 // /// @dev EVM selector for this function is: 0x50bb4e7f,508 // /// or in textual repr: mintWithTokenURI(address,uint256,string)509 // function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) public returns (bool) {510 // require(false, stub_error);511 // to;512 // tokenId;513 // tokenUri;514 // dummy = 0;515 // return false;516 // }454517455 /// @dev Not implemented518 /// @dev Not implemented456 /// @dev EVM selector for this function is: 0x7d64bcb4,519 /// @dev EVM selector for this function is: 0x7d64bcb4,463}526}464527465/// @title Unique extensions for ERC721.528/// @title Unique extensions for ERC721.466/// @dev the ERC-165 identifier for this interface is 0x7c3bef89529/// @dev the ERC-165 identifier for this interface is 0xef1eaacb467contract ERC721UniqueExtensions is Dummy, ERC165 {530contract ERC721UniqueExtensions is Dummy, ERC165 {531 /// @notice A descriptive name for a collection of NFTs in this contract532 /// @dev EVM selector for this function is: 0x06fdde03,533 /// or in textual repr: name()534 function name() public view returns (string memory) {535 require(false, stub_error);536 dummy;537 return "";538 }539540 /// @notice An abbreviated name for NFTs in this contract541 /// @dev EVM selector for this function is: 0x95d89b41,542 /// or in textual repr: symbol()543 function symbol() public view returns (string memory) {544 require(false, stub_error);545 dummy;546 return "";547 }548468 /// @notice Transfer ownership of an RFT549 /// @notice Transfer ownership of an RFT469 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`550 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`527 /// @param tokens array of pairs of token ID and token URI for minted tokens608 /// @param tokens array of pairs of token ID and token URI for minted tokens528 /// @dev EVM selector for this function is: 0x36543006,609 /// @dev EVM selector for this function is: 0x36543006,529 /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])610 /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])530 function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) public returns (bool) {611 function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) public returns (bool) {531 require(false, stub_error);612 require(false, stub_error);532 to;613 to;533 tokens;614 tokens;549}630}550631551/// @dev anonymous struct632/// @dev anonymous struct552struct Tuple8 {633struct Tuple6 {553 uint256 field_0;634 uint256 field_0;554 string field_1;635 string field_1;555}636}594 }675 }595}676}596597/// @dev the ERC-165 identifier for this interface is 0x5b5e139f598contract ERC721Metadata is Dummy, ERC165 {599 /// @notice A descriptive name for a collection of RFTs in this contract600 /// @dev EVM selector for this function is: 0x06fdde03,601 /// or in textual repr: name()602 function name() public view returns (string memory) {603 require(false, stub_error);604 dummy;605 return "";606 }607608 /// @notice An abbreviated name for RFTs in this contract609 /// @dev EVM selector for this function is: 0x95d89b41,610 /// or in textual repr: symbol()611 function symbol() public view returns (string memory) {612 require(false, stub_error);613 dummy;614 return "";615 }616617 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.618 ///619 /// @dev If the token has a `url` property and it is not empty, it is returned.620 /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.621 /// If the collection property `baseURI` is empty or absent, return "" (empty string)622 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix623 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).624 ///625 /// @return token's const_metadata626 /// @dev EVM selector for this function is: 0xc87b56dd,627 /// or in textual repr: tokenURI(uint256)628 function tokenURI(uint256 tokenId) public view returns (string memory) {629 require(false, stub_error);630 tokenId;631 dummy;632 return "";633 }634}635677636/// @dev inlined interface678/// @dev inlined interface637contract ERC721Events {679contract ERC721Events {776 Dummy,818 Dummy,777 ERC165,819 ERC165,778 ERC721,820 ERC721,779 ERC721Metadata,780 ERC721Enumerable,821 ERC721Enumerable,781 ERC721UniqueExtensions,822 ERC721UniqueExtensions,782 ERC721Mintable,823 ERC721UniqueMintable,783 ERC721Burnable,824 ERC721Burnable,825 ERC721Metadata,784 Collection,826 Collection,785 TokenProperties827 TokenProperties786{}828{}pallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/mod.rsdiffbeforeafterboth25 dispatch::CollectionDispatch,25 dispatch::CollectionDispatch,26 erc::{26 erc::{27 CollectionHelpersEvents,27 CollectionHelpersEvents,28 static_property::{key, value as property_value},28 static_property::{key},29 },29 },30 Pallet as PalletCommon,30};31};31use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};32use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};32use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};33use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};34use sp_std::vec;33use up_data_structs::{35use up_data_structs::{34 CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData,36 CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData,35 CollectionMode, PropertyValue,37 CollectionMode, PropertyValue, CollectionFlags,36};38};373938use crate::{Config, SelfWeightOf, weights::WeightInfo};40use crate::{Config, SelfWeightOf, weights::WeightInfo};57 name: string,59 name: string,58 description: string,60 description: string,59 token_prefix: string,61 token_prefix: string,60 base_uri: string,61) -> Result<(62) -> Result<(62 T::CrossAccountId,63 T::CrossAccountId,63 CollectionName,64 CollectionName,64 CollectionDescription,65 CollectionDescription,65 CollectionTokenPrefix,66 CollectionTokenPrefix,66 PropertyValue,67)> {67)> {68 let caller = T::CrossAccountId::from_eth(caller);68 let caller = T::CrossAccountId::from_eth(caller);69 let name = name69 let name = name81 let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {81 let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {82 error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())82 error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())83 })?;83 })?;84 let base_uri_value = base_uri85 .into_bytes()86 .try_into()87 .map_err(|_| error_field_too_long(stringify!(token_prefix), PropertyValue::bound()))?;88 Ok((caller, name, description, token_prefix, base_uri_value))84 Ok((caller, name, description, token_prefix))89}85}9091fn make_data<T: Config>(92 name: CollectionName,93 mode: CollectionMode,94 description: CollectionDescription,95 token_prefix: CollectionTokenPrefix,96 base_uri_value: PropertyValue,97 add_properties: bool,98) -> Result<CreateCollectionData<T::AccountId>> {99 let mut properties = up_data_structs::CollectionPropertiesVec::default();100 let mut token_property_permissions =101 up_data_structs::CollectionPropertiesPermissionsVec::default();102103 token_property_permissions104 .try_push(up_data_structs::PropertyKeyPermission {105 key: key::url(),106 permission: up_data_structs::PropertyPermission {107 mutable: false,108 collection_admin: true,109 token_owner: false,110 },111 })112 .map_err(|e| Error::Revert(format!("{:?}", e)))?;113114 if add_properties {115 token_property_permissions116 .try_push(up_data_structs::PropertyKeyPermission {117 key: key::suffix(),118 permission: up_data_structs::PropertyPermission {119 mutable: false,120 collection_admin: true,121 token_owner: false,122 },123 })124 .map_err(|e| Error::Revert(format!("{:?}", e)))?;125126 properties127 .try_push(up_data_structs::Property {128 key: key::schema_name(),129 value: property_value::erc721(),130 })131 .map_err(|e| Error::Revert(format!("{:?}", e)))?;132133 if !base_uri_value.is_empty() {134 properties135 .try_push(up_data_structs::Property {136 key: key::base_uri(),137 value: base_uri_value,138 })139 .map_err(|e| Error::Revert(format!("{:?}", e)))?;140 }141 }142143 let data = CreateCollectionData {144 name,145 mode,146 description,147 token_prefix,148 token_property_permissions,149 properties,150 ..Default::default()151 };152 Ok(data)153}15486155fn create_refungible_collection_internal<87fn create_refungible_collection_internal<156 T: Config + pallet_nonfungible::Config + pallet_refungible::Config,88 T: Config + pallet_nonfungible::Config + pallet_refungible::Config,160 name: string,92 name: string,161 description: string,93 description: string,162 token_prefix: string,94 token_prefix: string,163 base_uri: string,164 add_properties: bool,165) -> Result<address> {95) -> Result<address> {166 let (caller, name, description, token_prefix, base_uri_value) =96 let (caller, name, description, token_prefix) =167 convert_data::<T>(caller, name, description, token_prefix, base_uri)?;97 convert_data::<T>(caller, name, description, token_prefix)?;168 let data = make_data::<T>(98 let data = CreateCollectionData {169 name,99 name,170 CollectionMode::ReFungible,100 mode: CollectionMode::ReFungible,171 description,101 description,172 token_prefix,102 token_prefix,173 base_uri_value,103 ..Default::default()174 add_properties,175 )?;104 };176 check_sent_amount_equals_collection_creation_price::<T>(value)?;105 check_sent_amount_equals_collection_creation_price::<T>(value)?;177 let collection_helpers_address =106 let collection_helpers_address =178 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());107 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());179108180 let collection_id =109 let collection_id = T::CollectionDispatch::create(181 T::CollectionDispatch::create(caller.clone(), collection_helpers_address, data)110 caller.clone(),111 collection_helpers_address,112 data,113 Default::default(),114 )182 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;115 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;183 let address = pallet_common::eth::collection_id_to_address(collection_id);116 let address = pallet_common::eth::collection_id_to_address(collection_id);212 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications145 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications213 /// @return address Address of the newly created collection146 /// @return address Address of the newly created collection214 #[weight(<SelfWeightOf<T>>::create_collection())]147 #[weight(<SelfWeightOf<T>>::create_collection())]148 #[solidity(rename_selector = "createNFTCollection")]215 fn create_nonfungible_collection(149 fn create_nft_collection(216 &mut self,150 &mut self,217 caller: caller,151 caller: caller,218 value: value,152 value: value,219 name: string,153 name: string,220 description: string,154 description: string,221 token_prefix: string,155 token_prefix: string,222 ) -> Result<address> {156 ) -> Result<address> {223 let (caller, name, description, token_prefix, _base_uri_value) =157 let (caller, name, description, token_prefix) =224 convert_data::<T>(caller, name, description, token_prefix, "".into())?;158 convert_data::<T>(caller, name, description, token_prefix)?;225 let data = make_data::<T>(159 let data = CreateCollectionData {226 name,160 name,227 CollectionMode::NFT,161 mode: CollectionMode::NFT,228 description,162 description,229 token_prefix,163 token_prefix,230 Default::default(),164 ..Default::default()231 false,232 )?;165 };233 check_sent_amount_equals_collection_creation_price::<T>(value)?;166 check_sent_amount_equals_collection_creation_price::<T>(value)?;234 let collection_helpers_address =167 let collection_helpers_address =235 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());168 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());236 let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)169 let collection_id = T::CollectionDispatch::create(170 caller,171 collection_helpers_address,172 data,173 Default::default(),174 )237 .map_err(dispatch_to_evm::<T>)?;175 .map_err(dispatch_to_evm::<T>)?;238176239 let address = pallet_common::eth::collection_id_to_address(collection_id);177 let address = pallet_common::eth::collection_id_to_address(collection_id);240 Ok(address)178 Ok(address)241 }179 }242180 /// Create an NFT collection181 /// @param name Name of the collection182 /// @param description Informative description of the collection183 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications184 /// @return address Address of the newly created collection243 #[weight(<SelfWeightOf<T>>::create_collection())]185 #[weight(<SelfWeightOf<T>>::create_collection())]244 #[solidity(rename_selector = "createERC721MetadataCompatibleCollection")]186 #[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]187 #[solidity(hide)]245 fn create_nonfungible_collection_with_properties(188 fn create_nonfungible_collection(246 &mut self,189 &mut self,247 caller: caller,190 caller: caller,248 value: value,191 value: value,249 name: string,192 name: string,250 description: string,193 description: string,251 token_prefix: string,194 token_prefix: string,252 base_uri: string,253 ) -> Result<address> {195 ) -> Result<address> {254 let (caller, name, description, token_prefix, base_uri_value) =196 self.create_nft_collection(caller, value, name, description, token_prefix)255 convert_data::<T>(caller, name, description, token_prefix, base_uri)?;256 let data = make_data::<T>(257 name,258 CollectionMode::NFT,259 description,260 token_prefix,261 base_uri_value,262 true,263 )?;264 check_sent_amount_equals_collection_creation_price::<T>(value)?;265 let collection_helpers_address =266 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());267 let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)268 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;269270 let address = pallet_common::eth::collection_id_to_address(collection_id);271 Ok(address)272 }197 }273198274 #[weight(<SelfWeightOf<T>>::create_collection())]199 #[weight(<SelfWeightOf<T>>::create_collection())]275 #[solidity(rename_selector = "createRFTCollection")]200 #[solidity(rename_selector = "createRFTCollection")]276 fn create_refungible_collection(201 fn create_rft_collection(277 &mut self,202 &mut self,278 caller: caller,203 caller: caller,279 value: value,204 value: value,286 value,287 name,288 description,289 token_prefix,290 Default::default(),291 false,292 )293 }210 }294211295 #[weight(<SelfWeightOf<T>>::create_collection())]212 #[solidity(rename_selector = "makeCollectionERC721MetadataCompatible")]296 #[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]297 fn create_refungible_collection_with_properties(213 fn make_collection_metadata_compatible(298 &mut self,214 &mut self,299 caller: caller,215 caller: caller,300 value: value,216 collection: address,301 name: string,217 base_uri: string,218 ) -> Result<()> {219 let caller = T::CrossAccountId::from_eth(caller);220 let collection =221 pallet_common::eth::map_eth_to_id(&collection).ok_or("not a collection address")?;222 let mut collection =223 <crate::CollectionHandle<T>>::new(collection).ok_or("collection not found")?;224225 if !matches!(226 collection.mode,227 CollectionMode::NFT | CollectionMode::ReFungible228 ) {229 return Err("target collection should be either NFT or Refungible".into());230 }231232 self.recorder().consume_sstore()?;233 collection234 .check_is_owner_or_admin(&caller)235 .map_err(dispatch_to_evm::<T>)?;236237 if collection.flags.erc721metadata {238 return Err("target collection is already Erc721Metadata compatible".into());239 }240 collection.flags.erc721metadata = true;241242 let all_permissions = <pallet_common::CollectionPropertyPermissions<T>>::get(collection.id);243 if all_permissions.get(&key::url()).is_none() {244 self.recorder().consume_sstore()?;245 <PalletCommon<T>>::set_property_permission(246 &collection,247 &caller,248 up_data_structs::PropertyKeyPermission {249 key: key::url(),250 permission: up_data_structs::PropertyPermission {302 description: string,251 mutable: true,303 token_prefix: string,252 collection_admin: true,304 base_uri: string,253 token_owner: false,254 },255 },256 )305 ) -> Result<address> {257 .map_err(dispatch_to_evm::<T>)?;258 }259 if all_permissions.get(&key::suffix()).is_none() {260 self.recorder().consume_sstore()?;306 create_refungible_collection_internal::<T>(261 <PalletCommon<T>>::set_property_permission(262 &collection,307 caller,263 &caller,264 up_data_structs::PropertyKeyPermission {308 value,265 key: key::suffix(),266 permission: up_data_structs::PropertyPermission {309 name,267 mutable: true,310 description,268 collection_admin: true,311 token_prefix,269 token_owner: false,312 base_uri,270 },313 true,271 },314 )272 )273 .map_err(dispatch_to_evm::<T>)?;315 }274 }275276 let all_properties = <pallet_common::CollectionProperties<T>>::get(collection.id);277 if all_properties.get(&key::base_uri()).is_none() && !base_uri.is_empty() {278 self.recorder().consume_sstore()?;279 <PalletCommon<T>>::set_collection_properties(280 &collection,281 &caller,282 vec![up_data_structs::Property {283 key: key::base_uri(),284 value: base_uri285 .into_bytes()286 .try_into()287 .map_err(|_| "base uri is too large")?,288 }],289 )290 .map_err(dispatch_to_evm::<T>)?;291 }292293 self.recorder().consume_sstore()?;294 collection.save().map_err(dispatch_to_evm::<T>)?;295296 Ok(())297 }316298317 /// Check if a collection exists299 /// Check if a collection exists318 /// @param collectionAddress Address of the collection in question300 /// @param collectionAddress Address of the collection in questionpallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth23}23}242425/// @title Contract, which allows users to operate with collections25/// @title Contract, which allows users to operate with collections26/// @dev the ERC-165 identifier for this interface is 0x5ad4f44026/// @dev the ERC-165 identifier for this interface is 0x5891863127contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {27contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {28 /// Create an NFT collection28 /// Create an NFT collection29 /// @param name Name of the collection29 /// @param name Name of the collection30 /// @param description Informative description of the collection30 /// @param description Informative description of the collection31 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications31 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications32 /// @return address Address of the newly created collection32 /// @return address Address of the newly created collection33 /// @dev EVM selector for this function is: 0xe34a6844,33 /// @dev EVM selector for this function is: 0x844af658,34 /// or in textual repr: createNonfungibleCollection(string,string,string)34 /// or in textual repr: createNFTCollection(string,string,string)35 function createNonfungibleCollection(35 function createNFTCollection(36 string memory name,36 string memory name,37 string memory description,37 string memory description,38 string memory tokenPrefix38 string memory tokenPrefix45 return 0x0000000000000000000000000000000000000000;45 return 0x0000000000000000000000000000000000000000;46 }46 }474748 // /// Create an NFT collection49 // /// @param name Name of the collection50 // /// @param description Informative description of the collection51 // /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications52 // /// @return address Address of the newly created collection48 /// @dev EVM selector for this function is: 0xa634a5f9,53 // /// @dev EVM selector for this function is: 0xe34a6844,49 /// or in textual repr: createERC721MetadataCompatibleCollection(string,string,string,string)54 // /// or in textual repr: createNonfungibleCollection(string,string,string)50 function createERC721MetadataCompatibleCollection(55 // function createNonfungibleCollection(string memory name, string memory description, string memory tokenPrefix) public payable returns (address) {51 string memory name,56 // require(false, stub_error);52 string memory description,57 // name;53 string memory tokenPrefix,58 // description;54 string memory baseUri59 // tokenPrefix;55 ) public payable returns (address) {60 // dummy = 0;56 require(false, stub_error);61 // return 0x0000000000000000000000000000000000000000;57 name;62 // }58 description;59 tokenPrefix;60 baseUri;61 dummy = 0;62 return 0x0000000000000000000000000000000000000000;63 }646365 /// @dev EVM selector for this function is: 0xab173450,64 /// @dev EVM selector for this function is: 0xab173450,66 /// or in textual repr: createRFTCollection(string,string,string)65 /// or in textual repr: createRFTCollection(string,string,string)77 return 0x0000000000000000000000000000000000000000;76 return 0x0000000000000000000000000000000000000000;78 }77 }797880 /// @dev EVM selector for this function is: 0xa5596388,79 /// @dev EVM selector for this function is: 0x85624258,81 /// or in textual repr: createERC721MetadataCompatibleRFTCollection(string,string,string,string)80 /// or in textual repr: makeCollectionERC721MetadataCompatible(address,string)82 function createERC721MetadataCompatibleRFTCollection(81 function makeCollectionERC721MetadataCompatible(address collection, string memory baseUri) public {83 string memory name,84 string memory description,85 string memory tokenPrefix,86 string memory baseUri87 ) public payable returns (address) {88 require(false, stub_error);82 require(false, stub_error);89 name;83 collection;90 description;91 tokenPrefix;92 baseUri;84 baseUri;93 dummy = 0;85 dummy = 0;94 return 0x0000000000000000000000000000000000000000;95 }86 }968797 /// Check if a collection exists88 /// Check if a collection existspallets/unique/src/lib.rsdiffbeforeafterboth345345346 // =========346 // =========347 let sender = T::CrossAccountId::from_sub(sender);347 let sender = T::CrossAccountId::from_sub(sender);348 let _id = T::CollectionDispatch::create(sender.clone(), sender, data)?;348 let _id = T::CollectionDispatch::create(sender.clone(), sender, data, Default::default())?;349349350 Ok(())350 Ok(())351 }351 }primitives/data-structs/src/lib.rsdiffbeforeafterboth365 /// Tokens in foreign collections can be transferred, but not burnt365 /// Tokens in foreign collections can be transferred, but not burnt366 #[bondrewd(bits = "0..1")]366 #[bondrewd(bits = "0..1")]367 pub foreign: bool,367 pub foreign: bool,368 /// Supports ERC721Metadata369 #[bondrewd(bits = "1..2")]370 pub erc721metadata: bool,368 /// External collections can't be managed using `unique` api371 /// External collections can't be managed using `unique` api369 #[bondrewd(bits = "7..8")]372 #[bondrewd(bits = "7..8")]370 pub external: bool,373 pub external: bool,371374372 #[bondrewd(reserve, bits = "1..7")]375 #[bondrewd(reserve, bits = "2..7")]373 pub reserved: u8,376 pub reserved: u8,374}377}375bondrewd_codec!(CollectionFlags);378bondrewd_codec!(CollectionFlags);434 pub meta_update_permission: MetaUpdatePermission,437 pub meta_update_permission: MetaUpdatePermission,435}438}439440#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]441#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]442pub struct RpcCollectionFlags {443 /// Is collection is foreign.444 pub foreign: bool,445 /// Collection supports ERC721Metadata.446 pub erc721metadata: bool,447}436448437/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).449/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).438#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]450#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]471 /// Is collection read only.483 /// Is collection read only.472 pub read_only: bool,484 pub read_only: bool,473485474 /// Is collection is foreign.486 /// Extra collection flags475 pub foreign: bool,487 pub flags: RpcCollectionFlags,476}488}477489478/// Data used for create collection.490/// Data used for create collection.runtime/common/dispatch.rsdiffbeforeafterboth31};31};32use up_data_structs::{32use up_data_structs::{33 CollectionMode, CreateCollectionData, MAX_DECIMAL_POINTS, mapping::TokenAddressMapping,33 CollectionMode, CreateCollectionData, MAX_DECIMAL_POINTS, mapping::TokenAddressMapping,34 CollectionId,34 CollectionId, CollectionFlags,35};35};363637#[cfg(not(feature = "refungible"))]37#[cfg(not(feature = "refungible"))]57 sender: T::CrossAccountId,57 sender: T::CrossAccountId,58 payer: T::CrossAccountId,58 payer: T::CrossAccountId,59 data: CreateCollectionData<T::AccountId>,59 data: CreateCollectionData<T::AccountId>,60 flags: CollectionFlags,60 ) -> Result<CollectionId, DispatchError> {61 ) -> Result<CollectionId, DispatchError> {61 let id = match data.mode {62 let id = match data.mode {62 CollectionMode::NFT => {63 CollectionMode::NFT => {63 <PalletNonfungible<T>>::init_collection(sender, payer, data, false)?64 <PalletNonfungible<T>>::init_collection(sender, payer, data, flags)?64 }65 }65 CollectionMode::Fungible(decimal_points) => {66 CollectionMode::Fungible(decimal_points) => {66 // check params67 // check params67 ensure!(68 ensure!(68 decimal_points <= MAX_DECIMAL_POINTS,69 decimal_points <= MAX_DECIMAL_POINTS,69 pallet_unique::Error::<T>::CollectionDecimalPointLimitExceeded70 pallet_unique::Error::<T>::CollectionDecimalPointLimitExceeded70 );71 );71 <PalletFungible<T>>::init_collection(sender, payer, data)?72 <PalletFungible<T>>::init_collection(sender, payer, data, flags)?72 }73 }737474 #[cfg(feature = "refungible")]75 #[cfg(feature = "refungible")]75 CollectionMode::ReFungible => <PalletRefungible<T>>::init_collection(sender, payer, data)?,76 CollectionMode::ReFungible => {77 <PalletRefungible<T>>::init_collection(sender, payer, data, flags)?78 }767977 #[cfg(not(feature = "refungible"))]80 #[cfg(not(feature = "refungible"))]78 CollectionMode::ReFungible => return unsupported!(T),81 CollectionMode::ReFungible => return unsupported!(T),runtime/common/ethereum/sponsoring.rsdiffbeforeafterboth24use pallet_nonfungible::{24use pallet_nonfungible::{25 Config as NonfungibleConfig,25 Config as NonfungibleConfig,26 erc::{26 erc::{27 UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call,27 UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721UniqueMintableCall, ERC721Call,28 TokenPropertiesCall,28 TokenPropertiesCall,29 },29 },30};30};82 let token_id: TokenId = token_id.try_into().ok()?;82 let token_id: TokenId = token_id.try_into().ok()?;83 withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)83 withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)84 }84 }85 UniqueNFTCall::ERC721Mintable(85 UniqueNFTCall::ERC721UniqueMintable(86 ERC721MintableCall::Mint { token_id, .. }86 ERC721UniqueMintableCall::Mint { .. }87 | ERC721UniqueMintableCall::MintCheckId { .. }87 | ERC721MintableCall::MintWithTokenUri { token_id, .. },88 | ERC721UniqueMintableCall::MintWithTokenUri { .. }89 | ERC721UniqueMintableCall::MintWithTokenUriCheckId { .. },88 ) => {90 ) => withdraw_create_item::<T>(89 let _token_id: TokenId = token_id.try_into().ok()?;90 withdraw_create_item::<T>(91 &collection,91 &collection,92 &who,92 &who,93 &CreateItemData::NFT(CreateNftData::default()),93 &CreateItemData::NFT(CreateNftData::default()),94 )94 )95 .map(|()| sponsor)95 .map(|()| sponsor),96 }97 UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, from, .. }) => {96 UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, from, .. }) => {98 let token_id: TokenId = token_id.try_into().ok()?;97 let token_id: TokenId = token_id.try_into().ok()?;99 let from = T::CrossAccountId::from_eth(from);98 let from = T::CrossAccountId::from_eth(from);tests/src/eth/allowlist.test.tsdiffbeforeafterboth78 const owner = await helper.eth.createAccountWithBalance(donor);78 const owner = await helper.eth.createAccountWithBalance(donor);79 const user = helper.eth.createAccount();79 const user = helper.eth.createAccount();808081 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');81 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');82 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);82 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);838384 expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;84 expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;94 // const owner = await helper.eth.createAccountWithBalance(donor);94 // const owner = await helper.eth.createAccountWithBalance(donor);95 // const user = donor;95 // const user = donor;969697 // const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');97 // const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');98 // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);98 // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);9999100 // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;100 // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;110 const notOwner = await helper.eth.createAccountWithBalance(donor);110 const notOwner = await helper.eth.createAccountWithBalance(donor);111 const user = helper.eth.createAccount();111 const user = helper.eth.createAccount();112112113 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');113 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');114 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);114 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);115115116 expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;116 expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;129 // const notOwner = await helper.eth.createAccountWithBalance(donor);129 // const notOwner = await helper.eth.createAccountWithBalance(donor);130 // const user = donor;130 // const user = donor;131131132 // const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');132 // const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');133 // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);133 // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);134134135 // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;135 // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;tests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth18}18}191920/// @title Contract, which allows users to operate with collections20/// @title Contract, which allows users to operate with collections21/// @dev the ERC-165 identifier for this interface is 0x5ad4f44021/// @dev the ERC-165 identifier for this interface is 0x5891863122interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {22interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {23 /// Create an NFT collection23 /// Create an NFT collection24 /// @param name Name of the collection24 /// @param name Name of the collection25 /// @param description Informative description of the collection25 /// @param description Informative description of the collection26 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications26 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications27 /// @return address Address of the newly created collection27 /// @return address Address of the newly created collection28 /// @dev EVM selector for this function is: 0xe34a6844,28 /// @dev EVM selector for this function is: 0x844af658,29 /// or in textual repr: createNonfungibleCollection(string,string,string)29 /// or in textual repr: createNFTCollection(string,string,string)30 function createNonfungibleCollection(30 function createNFTCollection(31 string memory name,31 string memory name,32 string memory description,32 string memory description,33 string memory tokenPrefix33 string memory tokenPrefix34 ) external payable returns (address);34 ) external payable returns (address);353536 // /// Create an NFT collection37 // /// @param name Name of the collection38 // /// @param description Informative description of the collection39 // /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications40 // /// @return address Address of the newly created collection36 /// @dev EVM selector for this function is: 0xa634a5f9,41 // /// @dev EVM selector for this function is: 0xe34a6844,37 /// or in textual repr: createERC721MetadataCompatibleCollection(string,string,string,string)42 // /// or in textual repr: createNonfungibleCollection(string,string,string)38 function createERC721MetadataCompatibleCollection(43 // function createNonfungibleCollection(string memory name, string memory description, string memory tokenPrefix) external payable returns (address);39 string memory name,40 string memory description,41 string memory tokenPrefix,42 string memory baseUri43 ) external payable returns (address);444445 /// @dev EVM selector for this function is: 0xab173450,45 /// @dev EVM selector for this function is: 0xab173450,46 /// or in textual repr: createRFTCollection(string,string,string)46 /// or in textual repr: createRFTCollection(string,string,string)50 string memory tokenPrefix50 string memory tokenPrefix51 ) external payable returns (address);51 ) external payable returns (address);525253 /// @dev EVM selector for this function is: 0xa5596388,53 /// @dev EVM selector for this function is: 0x85624258,54 /// or in textual repr: createERC721MetadataCompatibleRFTCollection(string,string,string,string)54 /// or in textual repr: makeCollectionERC721MetadataCompatible(address,string)55 function createERC721MetadataCompatibleRFTCollection(55 function makeCollectionERC721MetadataCompatible(address collection, string memory baseUri) external;56 string memory name,57 string memory description,58 string memory tokenPrefix,59 string memory baseUri60 ) external payable returns (address);615662 /// Check if a collection exists57 /// Check if a collection exists63 /// @param collectionAddress Address of the collection in question58 /// @param collectionAddress Address of the collection in questiontests/src/eth/api/UniqueFungible.soldiffbeforeafterboth13}13}141415/// @title A contract that allows you to work with collections.15/// @title A contract that allows you to work with collections.16/// @dev the ERC-165 identifier for this interface is 0x3e1e808316/// @dev the ERC-165 identifier for this interface is 0x62e2229017interface Collection is Dummy, ERC165 {17interface Collection is Dummy, ERC165 {18 /// Set collection property.18 /// Set collection property.19 ///19 ///194 ///194 ///195 /// @dev Owner can be changed only by current owner195 /// @dev Owner can be changed only by current owner196 /// @param newOwner new owner account196 /// @param newOwner new owner account197 /// @dev EVM selector for this function is: 0x13af4035,197 /// @dev EVM selector for this function is: 0x4f53e226,198 /// or in textual repr: setOwner(address)198 /// or in textual repr: changeCollectionOwner(address)199 function setOwner(address newOwner) external;199 function changeCollectionOwner(address newOwner) external;200}200}201201202/// @dev the ERC-165 identifier for this interface is 0x63034ac5202/// @dev the ERC-165 identifier for this interface is 0x63034ac5tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth62}62}636364/// @title A contract that allows you to work with collections.64/// @title A contract that allows you to work with collections.65/// @dev the ERC-165 identifier for this interface is 0x3e1e808365/// @dev the ERC-165 identifier for this interface is 0x62e2229066interface Collection is Dummy, ERC165 {66interface Collection is Dummy, ERC165 {67 /// Set collection property.67 /// Set collection property.68 ///68 ///243 ///243 ///244 /// @dev Owner can be changed only by current owner244 /// @dev Owner can be changed only by current owner245 /// @param newOwner new owner account245 /// @param newOwner new owner account246 /// @dev EVM selector for this function is: 0x13af4035,246 /// @dev EVM selector for this function is: 0x4f53e226,247 /// or in textual repr: setOwner(address)247 /// or in textual repr: changeCollectionOwner(address)248 function setOwner(address newOwner) external;248 function changeCollectionOwner(address newOwner) external;249}249}250250251/// @dev anonymous struct251/// @dev anonymous struct254 uint256 field_1;254 uint256 field_1;255}255}256257/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension258/// @dev See https://eips.ethereum.org/EIPS/eip-721259/// @dev the ERC-165 identifier for this interface is 0x5b5e139f260interface ERC721Metadata is Dummy, ERC165 {261 // /// @notice A descriptive name for a collection of NFTs in this contract262 // /// @dev real implementation of this function lies in `ERC721UniqueExtensions`263 // /// @dev EVM selector for this function is: 0x06fdde03,264 // /// or in textual repr: name()265 // function name() external view returns (string memory);266267 // /// @notice An abbreviated name for NFTs in this contract268 // /// @dev real implementation of this function lies in `ERC721UniqueExtensions`269 // /// @dev EVM selector for this function is: 0x95d89b41,270 // /// or in textual repr: symbol()271 // function symbol() external view returns (string memory);272273 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.274 ///275 /// @dev If the token has a `url` property and it is not empty, it is returned.276 /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.277 /// If the collection property `baseURI` is empty or absent, return "" (empty string)278 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix279 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).280 ///281 /// @return token's const_metadata282 /// @dev EVM selector for this function is: 0xc87b56dd,283 /// or in textual repr: tokenURI(uint256)284 function tokenURI(uint256 tokenId) external view returns (string memory);285}256286257/// @title ERC721 Token that can be irreversibly burned (destroyed).287/// @title ERC721 Token that can be irreversibly burned (destroyed).258/// @dev the ERC-165 identifier for this interface is 0x42966c68288/// @dev the ERC-165 identifier for this interface is 0x42966c68267}297}268298269/// @dev inlined interface299/// @dev inlined interface270interface ERC721MintableEvents {300interface ERC721UniqueMintableEvents {271 event MintingFinished();301 event MintingFinished();272}302}273303274/// @title ERC721 minting logic.304/// @title ERC721 minting logic.275/// @dev the ERC-165 identifier for this interface is 0x68ccfe89305/// @dev the ERC-165 identifier for this interface is 0x476ff149276interface ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {306interface ERC721UniqueMintable is Dummy, ERC165, ERC721UniqueMintableEvents {277 /// @dev EVM selector for this function is: 0x05d2035b,307 /// @dev EVM selector for this function is: 0x05d2035b,278 /// or in textual repr: mintingFinished()308 /// or in textual repr: mintingFinished()279 function mintingFinished() external view returns (bool);309 function mintingFinished() external view returns (bool);280310281 /// @notice Function to mint token.311 /// @notice Function to mint token.282 /// @dev `tokenId` should be obtained with `nextTokenId` method,283 /// unlike standard, you can't specify it manually284 /// @param to The new owner312 /// @param to The new owner285 /// @param tokenId ID of the minted NFT313 /// @return uint256 The id of the newly minted token286 /// @dev EVM selector for this function is: 0x40c10f19,314 /// @dev EVM selector for this function is: 0x6a627842,287 /// or in textual repr: mint(address,uint256)315 /// or in textual repr: mint(address)288 function mint(address to, uint256 tokenId) external returns (bool);316 function mint(address to) external returns (uint256);317318 // /// @notice Function to mint token.319 // /// @dev `tokenId` should be obtained with `nextTokenId` method,320 // /// unlike standard, you can't specify it manually321 // /// @param to The new owner322 // /// @param tokenId ID of the minted NFT323 // /// @dev EVM selector for this function is: 0x40c10f19,324 // /// or in textual repr: mint(address,uint256)325 // function mint(address to, uint256 tokenId) external returns (bool);289326290 /// @notice Function to mint token with the given tokenUri.327 /// @notice Function to mint token with the given tokenUri.291 /// @dev `tokenId` should be obtained with `nextTokenId` method,292 /// unlike standard, you can't specify it manually293 /// @param to The new owner328 /// @param to The new owner294 /// @param tokenId ID of the minted NFT295 /// @param tokenUri Token URI that would be stored in the NFT properties329 /// @param tokenUri Token URI that would be stored in the NFT properties330 /// @return uint256 The id of the newly minted token296 /// @dev EVM selector for this function is: 0x50bb4e7f,331 /// @dev EVM selector for this function is: 0x45c17782,297 /// or in textual repr: mintWithTokenURI(address,uint256,string)332 /// or in textual repr: mintWithTokenURI(address,string)298 function mintWithTokenURI(333 function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256);299 address to,334300 uint256 tokenId,335 // /// @notice Function to mint token with the given tokenUri.301 string memory tokenUri336 // /// @dev `tokenId` should be obtained with `nextTokenId` method,302 ) external returns (bool);337 // /// unlike standard, you can't specify it manually338 // /// @param to The new owner339 // /// @param tokenId ID of the minted NFT340 // /// @param tokenUri Token URI that would be stored in the NFT properties341 // /// @dev EVM selector for this function is: 0x50bb4e7f,342 // /// or in textual repr: mintWithTokenURI(address,uint256,string)343 // function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool);303344304 /// @dev Not implemented345 /// @dev Not implemented305 /// @dev EVM selector for this function is: 0x7d64bcb4,346 /// @dev EVM selector for this function is: 0x7d64bcb4,308}349}309350310/// @title Unique extensions for ERC721.351/// @title Unique extensions for ERC721.311/// @dev the ERC-165 identifier for this interface is 0xd74d154f352/// @dev the ERC-165 identifier for this interface is 0x4468500d312interface ERC721UniqueExtensions is Dummy, ERC165 {353interface ERC721UniqueExtensions is Dummy, ERC165 {354 /// @notice A descriptive name for a collection of NFTs in this contract355 /// @dev EVM selector for this function is: 0x06fdde03,356 /// or in textual repr: name()357 function name() external view returns (string memory);358359 /// @notice An abbreviated name for NFTs in this contract360 /// @dev EVM selector for this function is: 0x95d89b41,361 /// or in textual repr: symbol()362 function symbol() external view returns (string memory);363313 /// @notice Transfer ownership of an NFT364 /// @notice Transfer ownership of an NFT314 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`365 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`350 /// @param tokens array of pairs of token ID and token URI for minted tokens401 /// @param tokens array of pairs of token ID and token URI for minted tokens351 /// @dev EVM selector for this function is: 0x36543006,402 /// @dev EVM selector for this function is: 0x36543006,352 /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])403 /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])353 function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) external returns (bool);404 function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) external returns (bool);354}405}355406356/// @dev anonymous struct407/// @dev anonymous struct357struct Tuple8 {408struct Tuple6 {358 uint256 field_0;409 uint256 field_0;359 string field_1;410 string field_1;360}411}384 function totalSupply() external view returns (uint256);435 function totalSupply() external view returns (uint256);385}436}386387/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension388/// @dev See https://eips.ethereum.org/EIPS/eip-721389/// @dev the ERC-165 identifier for this interface is 0x5b5e139f390interface ERC721Metadata is Dummy, ERC165 {391 /// @notice A descriptive name for a collection of NFTs in this contract392 /// @dev EVM selector for this function is: 0x06fdde03,393 /// or in textual repr: name()394 function name() external view returns (string memory);395396 /// @notice An abbreviated name for NFTs in this contract397 /// @dev EVM selector for this function is: 0x95d89b41,398 /// or in textual repr: symbol()399 function symbol() external view returns (string memory);400401 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.402 ///403 /// @dev If the token has a `url` property and it is not empty, it is returned.404 /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.405 /// If the collection property `baseURI` is empty or absent, return "" (empty string)406 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix407 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).408 ///409 /// @return token's const_metadata410 /// @dev EVM selector for this function is: 0xc87b56dd,411 /// or in textual repr: tokenURI(uint256)412 function tokenURI(uint256 tokenId) external view returns (string memory);413}414437415/// @dev inlined interface438/// @dev inlined interface416interface ERC721Events {439interface ERC721Events {507 Dummy,530 Dummy,508 ERC165,531 ERC165,509 ERC721,532 ERC721,510 ERC721Metadata,511 ERC721Enumerable,533 ERC721Enumerable,512 ERC721UniqueExtensions,534 ERC721UniqueExtensions,513 ERC721Mintable,535 ERC721UniqueMintable,514 ERC721Burnable,536 ERC721Burnable,537 ERC721Metadata,515 Collection,538 Collection,516 TokenProperties539 TokenProperties517{}540{}tests/src/eth/api/UniqueRFT.soldiffbeforeafterbothno changes
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth62}62}636364/// @title A contract that allows you to work with collections.64/// @title A contract that allows you to work with collections.65/// @dev the ERC-165 identifier for this interface is 0x3e1e808365/// @dev the ERC-165 identifier for this interface is 0x62e2229066interface Collection is Dummy, ERC165 {66interface Collection is Dummy, ERC165 {67 /// Set collection property.67 /// Set collection property.68 ///68 ///243 ///243 ///244 /// @dev Owner can be changed only by current owner244 /// @dev Owner can be changed only by current owner245 /// @param newOwner new owner account245 /// @param newOwner new owner account246 /// @dev EVM selector for this function is: 0x13af4035,246 /// @dev EVM selector for this function is: 0x4f53e226,247 /// or in textual repr: setOwner(address)247 /// or in textual repr: changeCollectionOwner(address)248 function setOwner(address newOwner) external;248 function changeCollectionOwner(address newOwner) external;249}249}250250251/// @dev anonymous struct251/// @dev anonymous struct254 uint256 field_1;254 uint256 field_1;255}255}256257/// @dev the ERC-165 identifier for this interface is 0x5b5e139f258interface ERC721Metadata is Dummy, ERC165 {259 // /// @notice A descriptive name for a collection of NFTs in this contract260 // /// @dev real implementation of this function lies in `ERC721UniqueExtensions`261 // /// @dev EVM selector for this function is: 0x06fdde03,262 // /// or in textual repr: name()263 // function name() external view returns (string memory);264265 // /// @notice An abbreviated name for NFTs in this contract266 // /// @dev real implementation of this function lies in `ERC721UniqueExtensions`267 // /// @dev EVM selector for this function is: 0x95d89b41,268 // /// or in textual repr: symbol()269 // function symbol() external view returns (string memory);270271 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.272 ///273 /// @dev If the token has a `url` property and it is not empty, it is returned.274 /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.275 /// If the collection property `baseURI` is empty or absent, return "" (empty string)276 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix277 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).278 ///279 /// @return token's const_metadata280 /// @dev EVM selector for this function is: 0xc87b56dd,281 /// or in textual repr: tokenURI(uint256)282 function tokenURI(uint256 tokenId) external view returns (string memory);283}256284257/// @title ERC721 Token that can be irreversibly burned (destroyed).285/// @title ERC721 Token that can be irreversibly burned (destroyed).258/// @dev the ERC-165 identifier for this interface is 0x42966c68286/// @dev the ERC-165 identifier for this interface is 0x42966c68267}295}268296269/// @dev inlined interface297/// @dev inlined interface270interface ERC721MintableEvents {298interface ERC721UniqueMintableEvents {271 event MintingFinished();299 event MintingFinished();272}300}273301274/// @title ERC721 minting logic.302/// @title ERC721 minting logic.275/// @dev the ERC-165 identifier for this interface is 0x68ccfe89303/// @dev the ERC-165 identifier for this interface is 0x476ff149276interface ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {304interface ERC721UniqueMintable is Dummy, ERC165, ERC721UniqueMintableEvents {277 /// @dev EVM selector for this function is: 0x05d2035b,305 /// @dev EVM selector for this function is: 0x05d2035b,278 /// or in textual repr: mintingFinished()306 /// or in textual repr: mintingFinished()279 function mintingFinished() external view returns (bool);307 function mintingFinished() external view returns (bool);280308281 /// @notice Function to mint token.309 /// @notice Function to mint token.282 /// @dev `tokenId` should be obtained with `nextTokenId` method,283 /// unlike standard, you can't specify it manually284 /// @param to The new owner310 /// @param to The new owner285 /// @param tokenId ID of the minted RFT311 /// @return uint256 The id of the newly minted token286 /// @dev EVM selector for this function is: 0x40c10f19,312 /// @dev EVM selector for this function is: 0x6a627842,287 /// or in textual repr: mint(address,uint256)313 /// or in textual repr: mint(address)288 function mint(address to, uint256 tokenId) external returns (bool);314 function mint(address to) external returns (uint256);315316 // /// @notice Function to mint token.317 // /// @dev `tokenId` should be obtained with `nextTokenId` method,318 // /// unlike standard, you can't specify it manually319 // /// @param to The new owner320 // /// @param tokenId ID of the minted RFT321 // /// @dev EVM selector for this function is: 0x40c10f19,322 // /// or in textual repr: mint(address,uint256)323 // function mint(address to, uint256 tokenId) external returns (bool);289324290 /// @notice Function to mint token with the given tokenUri.325 /// @notice Function to mint token with the given tokenUri.291 /// @dev `tokenId` should be obtained with `nextTokenId` method,292 /// unlike standard, you can't specify it manually293 /// @param to The new owner326 /// @param to The new owner294 /// @param tokenId ID of the minted RFT295 /// @param tokenUri Token URI that would be stored in the RFT properties327 /// @param tokenUri Token URI that would be stored in the NFT properties328 /// @return uint256 The id of the newly minted token296 /// @dev EVM selector for this function is: 0x50bb4e7f,329 /// @dev EVM selector for this function is: 0x45c17782,297 /// or in textual repr: mintWithTokenURI(address,uint256,string)330 /// or in textual repr: mintWithTokenURI(address,string)298 function mintWithTokenURI(331 function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256);299 address to,332300 uint256 tokenId,333 // /// @notice Function to mint token with the given tokenUri.301 string memory tokenUri334 // /// @dev `tokenId` should be obtained with `nextTokenId` method,302 ) external returns (bool);335 // /// unlike standard, you can't specify it manually336 // /// @param to The new owner337 // /// @param tokenId ID of the minted RFT338 // /// @param tokenUri Token URI that would be stored in the RFT properties339 // /// @dev EVM selector for this function is: 0x50bb4e7f,340 // /// or in textual repr: mintWithTokenURI(address,uint256,string)341 // function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool);303342304 /// @dev Not implemented343 /// @dev Not implemented305 /// @dev EVM selector for this function is: 0x7d64bcb4,344 /// @dev EVM selector for this function is: 0x7d64bcb4,308}347}309348310/// @title Unique extensions for ERC721.349/// @title Unique extensions for ERC721.311/// @dev the ERC-165 identifier for this interface is 0x7c3bef89350/// @dev the ERC-165 identifier for this interface is 0xef1eaacb312interface ERC721UniqueExtensions is Dummy, ERC165 {351interface ERC721UniqueExtensions is Dummy, ERC165 {352 /// @notice A descriptive name for a collection of NFTs in this contract353 /// @dev EVM selector for this function is: 0x06fdde03,354 /// or in textual repr: name()355 function name() external view returns (string memory);356357 /// @notice An abbreviated name for NFTs in this contract358 /// @dev EVM selector for this function is: 0x95d89b41,359 /// or in textual repr: symbol()360 function symbol() external view returns (string memory);361313 /// @notice Transfer ownership of an RFT362 /// @notice Transfer ownership of an RFT314 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`363 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`352 /// @param tokens array of pairs of token ID and token URI for minted tokens401 /// @param tokens array of pairs of token ID and token URI for minted tokens353 /// @dev EVM selector for this function is: 0x36543006,402 /// @dev EVM selector for this function is: 0x36543006,354 /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])403 /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])355 function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) external returns (bool);404 function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) external returns (bool);356405357 /// Returns EVM address for refungible token406 /// Returns EVM address for refungible token358 ///407 ///363}412}364413365/// @dev anonymous struct414/// @dev anonymous struct366struct Tuple8 {415struct Tuple6 {367 uint256 field_0;416 uint256 field_0;368 string field_1;417 string field_1;369}418}393 function totalSupply() external view returns (uint256);442 function totalSupply() external view returns (uint256);394}443}395396/// @dev the ERC-165 identifier for this interface is 0x5b5e139f397interface ERC721Metadata is Dummy, ERC165 {398 /// @notice A descriptive name for a collection of RFTs in this contract399 /// @dev EVM selector for this function is: 0x06fdde03,400 /// or in textual repr: name()401 function name() external view returns (string memory);402403 /// @notice An abbreviated name for RFTs in this contract404 /// @dev EVM selector for this function is: 0x95d89b41,405 /// or in textual repr: symbol()406 function symbol() external view returns (string memory);407408 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.409 ///410 /// @dev If the token has a `url` property and it is not empty, it is returned.411 /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.412 /// If the collection property `baseURI` is empty or absent, return "" (empty string)413 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix414 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).415 ///416 /// @return token's const_metadata417 /// @dev EVM selector for this function is: 0xc87b56dd,418 /// or in textual repr: tokenURI(uint256)419 function tokenURI(uint256 tokenId) external view returns (string memory);420}421444422/// @dev inlined interface445/// @dev inlined interface423interface ERC721Events {446interface ERC721Events {512 Dummy,535 Dummy,513 ERC165,536 ERC165,514 ERC721,537 ERC721,515 ERC721Metadata,516 ERC721Enumerable,538 ERC721Enumerable,517 ERC721UniqueExtensions,539 ERC721UniqueExtensions,518 ERC721Mintable,540 ERC721UniqueMintable,519 ERC721Burnable,541 ERC721Burnable,542 ERC721Metadata,520 Collection,543 Collection,521 TokenProperties544 TokenProperties522{}545{}tests/src/eth/base.test.tsdiffbeforeafterboth69describe('ERC165 tests', async () => {74describe('ERC165 tests', async () => {70 // https://eips.ethereum.org/EIPS/eip-16575 // https://eips.ethereum.org/EIPS/eip-165717672 let collection: number;77 let erc721MetadataCompatibleNftCollectionId: number;78 let simpleNftCollectionId: number;73 let minter: string;79 let minter: string;8081 const BASE_URI = 'base/';748275 function contract(helper: EthUniqueHelper): Contract {83 async function checkInterface(helper: EthUniqueHelper, interfaceId: string, simpleResult: boolean, compatibleResult: boolean) {76 return helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection), 'nft', minter);84 const simple = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(simpleNftCollectionId), 'nft', minter);85 const compatible = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(erc721MetadataCompatibleNftCollectionId), 'nft', minter);8687 expect(await simple.methods.supportsInterface(interfaceId).call()).to.equal(simpleResult, `empty (not ERC721Metadata compatible) NFT collection returns not ${simpleResult}`);88 expect(await compatible.methods.supportsInterface(interfaceId).call()).to.equal(compatibleResult, `ERC721Metadata compatible NFT collection returns not ${compatibleResult}`);77 }89 }789079 before(async () => {91 before(async () => {80 await usingEthPlaygrounds(async (helper, privateKey) => {92 await usingEthPlaygrounds(async (helper, privateKey) => {81 const donor = await privateKey({filename: __filename});93 const donor = await privateKey({filename: __filename});82 const [alice] = await helper.arrange.createAccounts([10n], donor);94 const [alice] = await helper.arrange.createAccounts([10n], donor);83 ({collectionId: collection} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}));95 ({collectionId: simpleNftCollectionId} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}));84 minter = helper.eth.createAccount();96 minter = helper.eth.createAccount();97 ({collectionId: erc721MetadataCompatibleNftCollectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(minter, 'n', 'd', 'p', BASE_URI));85 });98 });86 });99 });8710088 itEth('interfaceID == 0xffffffff always false', async ({helper}) => {101 itEth('nonexistent interfaceID - 0xffffffff - always false', async ({helper}) => {89 expect(await contract(helper).methods.supportsInterface('0xffffffff').call()).to.be.false;102 await checkInterface(helper, '0xffffffff', false, false);90 });103 });9110492 itEth('ERC721 support', async ({helper}) => {105 itEth('ERC721 - 0x780e9d63 - support', async ({helper}) => {93 expect(await contract(helper).methods.supportsInterface('0x780e9d63').call()).to.be.true;106 await checkInterface(helper, '0x780e9d63', true, true);94 });107 });9510896 itEth('ERC721Metadata support', async ({helper}) => {109 itEth('ERC721Metadata - 0x5b5e139f - support', async ({helper}) => {97 expect(await contract(helper).methods.supportsInterface('0x5b5e139f').call()).to.be.true;110 await checkInterface(helper, '0x5b5e139f', false, true);98 });111 });99112100 itEth('ERC721Mintable support', async ({helper}) => {113 itEth('ERC721UniqueMintable - 0x476ff149 - support', async ({helper}) => {101 expect(await contract(helper).methods.supportsInterface('0x68ccfe89').call()).to.be.true;114 await checkInterface(helper, '0x476ff149', true, true);102 });115 });103116104 itEth('ERC721Enumerable support', async ({helper}) => {117 itEth('ERC721Enumerable - 0x780e9d63 - support', async ({helper}) => {105 expect(await contract(helper).methods.supportsInterface('0x780e9d63').call()).to.be.true;118 await checkInterface(helper, '0x780e9d63', true, true);106 });119 });107120108 itEth('ERC721UniqueExtensions support', async ({helper}) => {121 itEth('ERC721UniqueExtensions - 0x4468500d - support', async ({helper}) => {109 expect(await contract(helper).methods.supportsInterface('0xd74d154f').call()).to.be.true;122 await checkInterface(helper, '0x4468500d', true, true);110 });123 });111124112 itEth('ERC721Burnable support', async ({helper}) => {125 itEth('ERC721Burnable - 0x42966c68 - support', async ({helper}) => {113 expect(await contract(helper).methods.supportsInterface('0x42966c68').call()).to.be.true;126 await checkInterface(helper, '0x42966c68', true, true);114 });127 });115128116 itEth('ERC165 support', async ({helper}) => {129 itEth('ERC165 - 0x01ffc9a7 - support', async ({helper}) => {117 expect(await contract(helper).methods.supportsInterface('0x01ffc9a7').call()).to.be.true;130 await checkInterface(helper, '0x01ffc9a7', true, true);118 });131 });119});132});120133tests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth383839 itEth('Add admin by owner', async ({helper}) => {39 itEth('Add admin by owner', async ({helper}) => {40 const owner = await helper.eth.createAccountWithBalance(donor);40 const owner = await helper.eth.createAccountWithBalance(donor);41 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');41 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');42 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);42 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);434344 const newAdmin = helper.eth.createAccount();44 const newAdmin = helper.eth.createAccount();515152 itEth.skip('Add substrate admin by owner', async ({helper}) => {52 itEth.skip('Add substrate admin by owner', async ({helper}) => {53 const owner = await helper.eth.createAccountWithBalance(donor);53 const owner = await helper.eth.createAccountWithBalance(donor);54 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');54 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');55 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);55 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);565657 const [newAdmin] = await helper.arrange.createAccounts([10n], donor);57 const [newAdmin] = await helper.arrange.createAccounts([10n], donor);646465 itEth('Verify owner or admin', async ({helper}) => {65 itEth('Verify owner or admin', async ({helper}) => {66 const owner = await helper.eth.createAccountWithBalance(donor);66 const owner = await helper.eth.createAccountWithBalance(donor);67 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');67 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');686869 const newAdmin = helper.eth.createAccount();69 const newAdmin = helper.eth.createAccount();70 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);70 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);757576 itEth('(!negative tests!) Add admin by ADMIN is not allowed', async ({helper}) => {76 itEth('(!negative tests!) Add admin by ADMIN is not allowed', async ({helper}) => {77 const owner = await helper.eth.createAccountWithBalance(donor);77 const owner = await helper.eth.createAccountWithBalance(donor);78 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');78 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');797980 const admin = await helper.eth.createAccountWithBalance(donor);80 const admin = await helper.eth.createAccountWithBalance(donor);81 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);81 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);939394 itEth('(!negative tests!) Add admin by USER is not allowed', async ({helper}) => {94 itEth('(!negative tests!) Add admin by USER is not allowed', async ({helper}) => {95 const owner = await helper.eth.createAccountWithBalance(donor);95 const owner = await helper.eth.createAccountWithBalance(donor);96 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');96 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');979798 const notAdmin = await helper.eth.createAccountWithBalance(donor);98 const notAdmin = await helper.eth.createAccountWithBalance(donor);99 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);99 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);108108109 itEth.skip('(!negative tests!) Add substrate admin by ADMIN is not allowed', async ({helper}) => {109 itEth.skip('(!negative tests!) Add substrate admin by ADMIN is not allowed', async ({helper}) => {110 const owner = await helper.eth.createAccountWithBalance(donor);110 const owner = await helper.eth.createAccountWithBalance(donor);111 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');111 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');112112113 const admin = await helper.eth.createAccountWithBalance(donor);113 const admin = await helper.eth.createAccountWithBalance(donor);114 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);114 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);126126127 itEth.skip('(!negative tests!) Add substrate admin by USER is not allowed', async ({helper}) => {127 itEth.skip('(!negative tests!) Add substrate admin by USER is not allowed', async ({helper}) => {128 const owner = await helper.eth.createAccountWithBalance(donor);128 const owner = await helper.eth.createAccountWithBalance(donor);129 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');129 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');130130131 const notAdmin0 = await helper.eth.createAccountWithBalance(donor);131 const notAdmin0 = await helper.eth.createAccountWithBalance(donor);132 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);132 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);150150151 itEth('Remove admin by owner', async ({helper}) => {151 itEth('Remove admin by owner', async ({helper}) => {152 const owner = await helper.eth.createAccountWithBalance(donor);152 const owner = await helper.eth.createAccountWithBalance(donor);153 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');153 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');154154155 const newAdmin = helper.eth.createAccount();155 const newAdmin = helper.eth.createAccount();156 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);156 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);170170171 itEth.skip('Remove substrate admin by owner', async ({helper}) => {171 itEth.skip('Remove substrate admin by owner', async ({helper}) => {172 const owner = await helper.eth.createAccountWithBalance(donor);172 const owner = await helper.eth.createAccountWithBalance(donor);173 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');173 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');174174175 const [newAdmin] = await helper.arrange.createAccounts([10n], donor);175 const [newAdmin] = await helper.arrange.createAccounts([10n], donor);176 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);176 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);188188189 itEth('(!negative tests!) Remove admin by ADMIN is not allowed', async ({helper}) => {189 itEth('(!negative tests!) Remove admin by ADMIN is not allowed', async ({helper}) => {190 const owner = await helper.eth.createAccountWithBalance(donor);190 const owner = await helper.eth.createAccountWithBalance(donor);191 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');191 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');192192193 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);193 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);194194210210211 itEth('(!negative tests!) Remove admin by USER is not allowed', async ({helper}) => {211 itEth('(!negative tests!) Remove admin by USER is not allowed', async ({helper}) => {212 const owner = await helper.eth.createAccountWithBalance(donor);212 const owner = await helper.eth.createAccountWithBalance(donor);213 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');213 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');214214215 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);215 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);216216230230231 itEth.skip('(!negative tests!) Remove substrate admin by ADMIN is not allowed', async ({helper}) => {231 itEth.skip('(!negative tests!) Remove substrate admin by ADMIN is not allowed', async ({helper}) => {232 const owner = await helper.eth.createAccountWithBalance(donor);232 const owner = await helper.eth.createAccountWithBalance(donor);233 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');233 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');234234235 const [adminSub] = await helper.arrange.createAccounts([10n], donor);235 const [adminSub] = await helper.arrange.createAccounts([10n], donor);236 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);236 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);250250251 itEth.skip('(!negative tests!) Remove substrate admin by USER is not allowed', async ({helper}) => {251 itEth.skip('(!negative tests!) Remove substrate admin by USER is not allowed', async ({helper}) => {252 const owner = await helper.eth.createAccountWithBalance(donor);252 const owner = await helper.eth.createAccountWithBalance(donor);253 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');253 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');254254255 const [adminSub] = await helper.arrange.createAccounts([10n], donor);255 const [adminSub] = await helper.arrange.createAccounts([10n], donor);256 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);256 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);279 itEth('Change owner', async ({helper}) => {279 itEth('Change owner', async ({helper}) => {280 const owner = await helper.eth.createAccountWithBalance(donor);280 const owner = await helper.eth.createAccountWithBalance(donor);281 const newOwner = await helper.eth.createAccountWithBalance(donor);281 const newOwner = await helper.eth.createAccountWithBalance(donor);282 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');282 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');283 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);283 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);284284285 await collectionEvm.methods.setOwner(newOwner).send();285 await collectionEvm.methods.changeCollectionOwner(newOwner).send();286286287 expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.false;287 expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.false;288 expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.true;288 expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.true;291 itEth('change owner call fee', async ({helper}) => {291 itEth('change owner call fee', async ({helper}) => {292 const owner = await helper.eth.createAccountWithBalance(donor);292 const owner = await helper.eth.createAccountWithBalance(donor);293 const newOwner = await helper.eth.createAccountWithBalance(donor);293 const newOwner = await helper.eth.createAccountWithBalance(donor);294 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');294 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');295 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);295 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);296 const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwner(newOwner).send());296 const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.changeCollectionOwner(newOwner).send());297 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));297 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));298 expect(cost > 0);298 expect(cost > 0);299 });299 });300300301 itEth('(!negative tests!) call setOwner by non owner', async ({helper}) => {301 itEth('(!negative tests!) call setOwner by non owner', async ({helper}) => {302 const owner = await helper.eth.createAccountWithBalance(donor);302 const owner = await helper.eth.createAccountWithBalance(donor);303 const newOwner = await helper.eth.createAccountWithBalance(donor);303 const newOwner = await helper.eth.createAccountWithBalance(donor);304 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');304 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');305 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);305 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);306306307 await expect(collectionEvm.methods.setOwner(newOwner).send({from: newOwner})).to.be.rejected;307 await expect(collectionEvm.methods.changeCollectionOwner(newOwner).send({from: newOwner})).to.be.rejected;308 expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.false;308 expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.false;309 });309 });310});310});321 itEth.skip('Change owner', async ({helper}) => {321 itEth.skip('Change owner', async ({helper}) => {322 const owner = await helper.eth.createAccountWithBalance(donor);322 const owner = await helper.eth.createAccountWithBalance(donor);323 const [newOwner] = await helper.arrange.createAccounts([10n], donor);323 const [newOwner] = await helper.arrange.createAccounts([10n], donor);324 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');324 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');325 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);325 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);326326327 expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.true;327 expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.true;336 itEth.skip('change owner call fee', async ({helper}) => {336 itEth.skip('change owner call fee', async ({helper}) => {337 const owner = await helper.eth.createAccountWithBalance(donor);337 const owner = await helper.eth.createAccountWithBalance(donor);338 const [newOwner] = await helper.arrange.createAccounts([10n], donor);338 const [newOwner] = await helper.arrange.createAccounts([10n], donor);339 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');339 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');340 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);340 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);341341342 const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send());342 const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send());348 const owner = await helper.eth.createAccountWithBalance(donor);348 const owner = await helper.eth.createAccountWithBalance(donor);349 const otherReceiver = await helper.eth.createAccountWithBalance(donor);349 const otherReceiver = await helper.eth.createAccountWithBalance(donor);350 const [newOwner] = await helper.arrange.createAccounts([10n], donor);350 const [newOwner] = await helper.arrange.createAccounts([10n], donor);351 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');351 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');352 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);352 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);353353354 await expect(collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send({from: otherReceiver})).to.be.rejected;354 await expect(collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send({from: otherReceiver})).to.be.rejected;tests/src/eth/collectionHelpersAbi.jsondiffbeforeafterboth25 "stateMutability": "view",25 "stateMutability": "view",26 "type": "function"26 "type": "function"27 },27 },28 {29 "inputs": [30 { "internalType": "string", "name": "name", "type": "string" },31 { "internalType": "string", "name": "description", "type": "string" },32 { "internalType": "string", "name": "tokenPrefix", "type": "string" },33 { "internalType": "string", "name": "baseUri", "type": "string" }34 ],35 "name": "createERC721MetadataCompatibleCollection",36 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],37 "stateMutability": "payable",38 "type": "function"39 },40 {41 "inputs": [42 { "internalType": "string", "name": "name", "type": "string" },43 { "internalType": "string", "name": "description", "type": "string" },44 { "internalType": "string", "name": "tokenPrefix", "type": "string" },45 { "internalType": "string", "name": "baseUri", "type": "string" }46 ],47 "name": "createERC721MetadataCompatibleRFTCollection",48 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],49 "stateMutability": "payable",50 "type": "function"51 },52 {28 {53 "inputs": [29 "inputs": [54 { "internalType": "string", "name": "name", "type": "string" },30 { "internalType": "string", "name": "name", "type": "string" },55 { "internalType": "string", "name": "description", "type": "string" },31 { "internalType": "string", "name": "description", "type": "string" },56 { "internalType": "string", "name": "tokenPrefix", "type": "string" }32 { "internalType": "string", "name": "tokenPrefix", "type": "string" }57 ],33 ],58 "name": "createNonfungibleCollection",34 "name": "createNFTCollection",59 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],35 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],60 "stateMutability": "payable",36 "stateMutability": "payable",61 "type": "function"37 "type": "function"84 "stateMutability": "view",60 "stateMutability": "view",85 "type": "function"61 "type": "function"86 },62 },63 {64 "inputs": [65 { "internalType": "address", "name": "collection", "type": "address" },66 { "internalType": "string", "name": "baseUri", "type": "string" }67 ],68 "name": "makeCollectionERC721MetadataCompatible",69 "outputs": [],70 "stateMutability": "nonpayable",71 "type": "function"72 },87 {73 {88 "inputs": [74 "inputs": [89 { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }75 { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }tests/src/eth/collectionProperties.test.tsdiffbeforeafterboth14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.161617import {itEth, usingEthPlaygrounds, expect} from './util';17import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util';18import {Pallets} from '../util';19import {IProperty, ITokenPropertyPermission} from '../util/playgrounds/types';18import {IKeyringPair} from '@polkadot/types/types';20import {IKeyringPair} from '@polkadot/types/types';21import {Contract} from 'web3-eth-contract';192220describe('EVM collection properties', () => {23describe('EVM collection properties', () => {21 let donor: IKeyringPair;24 let donor: IKeyringPair;303331 itEth('Can be set', async({helper}) => {34 itEth('Can be set', async({helper}) => {32 const caller = await helper.eth.createAccountWithBalance(donor);35 const caller = await helper.eth.createAccountWithBalance(donor);33 const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test'});36 const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []});34 await collection.addAdmin(alice, {Ethereum: caller});37 await collection.addAdmin(alice, {Ethereum: caller});353836 const address = helper.ethAddress.fromCollectionId(collection.collectionId);39 const address = helper.ethAddress.fromCollectionId(collection.collectionId);71 });74 });72});75});7677describe('Supports ERC721Metadata', () => {78 let donor: IKeyringPair;7980 before(async function() {81 await usingEthPlaygrounds(async (_helper, privateKey) => {82 donor = await privateKey({filename: __filename});83 });84 });8586 const checkERC721Metadata = async (helper: EthUniqueHelper, mode: 'nft' | 'rft') => {87 const caller = await helper.eth.createAccountWithBalance(donor);88 const bruh = await helper.eth.createAccountWithBalance(donor);8990 const BASE_URI = 'base/'91 const SUFFIX = 'suffix1'92 const URI = 'uri1'9394 const collectionHelpers = helper.ethNativeContract.collectionHelpers(caller);95 const creatorMethod = mode === 'rft' ? 'createRFTCollection' : 'createNFTCollection'9697 const {collectionId, collectionAddress} = await helper.eth[creatorMethod](caller, 'n', 'd', 'p')9899 const contract = helper.ethNativeContract.collectionById(collectionId, mode, caller);100 await contract.methods.addCollectionAdmin(bruh).send(); // to check that admin will work too101102 const collection1 = await helper.nft.getCollectionObject(collectionId);103 const data1 = await collection1.getData()104 expect(data1?.raw.flags.erc721metadata).to.be.false;105 expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false;106107 await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, BASE_URI)108 .send({from: bruh});109110 expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true;111112 const collection2 = await helper.nft.getCollectionObject(collectionId);113 const data2 = await collection2.getData()114 expect(data2?.raw.flags.erc721metadata).to.be.true;115116 const TPPs = data2?.raw.tokenPropertyPermissions117 expect(TPPs?.length).to.equal(2);118119 expect(TPPs.find((tpp: ITokenPropertyPermission) => {120 return tpp.key === "URI" && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner121 })).to.be.not.null122123 expect(TPPs.find((tpp: ITokenPropertyPermission) => {124 return tpp.key === "URISuffix" && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner125 })).to.be.not.null126127 expect(data2?.raw.properties?.find((property: IProperty) => {128 return property.key === "baseURI" && property.value === BASE_URI129 })).to.be.not.null130131 const token1Result = await contract.methods.mint(bruh).send();132 const tokenId1 = token1Result.events.Transfer.returnValues.tokenId;133134 expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI);135136 await contract.methods.setProperty(tokenId1, "URISuffix", Buffer.from(SUFFIX)).send();137 expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX);138139 await contract.methods.setProperty(tokenId1, "URI", Buffer.from(URI)).send();140 expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(URI);141142 await contract.methods.deleteProperty(tokenId1, "URI").send();143 expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX);144145 const token2Result = await contract.methods.mintWithTokenURI(bruh, URI).send();146 const tokenId2 = token2Result.events.Transfer.returnValues.tokenId;147148 expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(URI);149150 await contract.methods.deleteProperty(tokenId2, "URI").send();151 expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI);152153 await contract.methods.setProperty(tokenId2, "URISuffix", Buffer.from(SUFFIX)).send();154 expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI + SUFFIX);155 }156157 itEth('ERC721Metadata property can be set for NFT collection', async({helper}) => {158 await checkERC721Metadata(helper, 'nft');159 });160161 itEth.ifWithPallets('ERC721Metadata property can be set for RFT collection', [Pallets.ReFungible], async({helper}) => {162 await checkERC721Metadata(helper, 'rft');163 });164});73165tests/src/eth/collectionSponsoring.test.tsdiffbeforeafterboth444445 await collection.addToAllowList(alice, {Ethereum: minter});45 await collection.addToAllowList(alice, {Ethereum: minter});464647 const nextTokenId = await contract.methods.nextTokenId().call();48 expect(nextTokenId).to.equal('1');49 const result = await contract.methods.mint(minter, nextTokenId).send();47 const result = await contract.methods.mint(minter).send();4850 const events = helper.eth.normalizeEvents(result.events);49 const events = helper.eth.normalizeEvents(result.events);51 expect(events).to.be.deep.equal([50 expect(events).to.be.deep.equal([55 args: {54 args: {56 from: '0x0000000000000000000000000000000000000000',55 from: '0x0000000000000000000000000000000000000000',57 to: minter,56 to: minter,58 tokenId: nextTokenId,57 tokenId: '1',59 },58 },60 },59 },61 ]);60 ]);65 // itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => {64 // itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => {66 // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);65 // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);67 // const collectionHelpers = evmCollectionHelpers(web3, owner);66 // const collectionHelpers = evmCollectionHelpers(web3, owner);68 // let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();67 // let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send();69 // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);68 // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);70 // const sponsor = privateKeyWrapper('//Alice');69 // const sponsor = privateKeyWrapper('//Alice');71 // const collectionEvm = evmCollection(web3, owner, collectionIdAddress);70 // const collectionEvm = evmCollection(web3, owner, collectionIdAddress);86 const owner = await helper.eth.createAccountWithBalance(donor);85 const owner = await helper.eth.createAccountWithBalance(donor);87 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);86 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);888789 let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});88 let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});90 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);89 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);91 const sponsor = await helper.eth.createAccountWithBalance(donor);90 const sponsor = await helper.eth.createAccountWithBalance(donor);92 const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner);91 const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner);107 itEth('Sponsoring collection from evm address via access list', async ({helper}) => {106 itEth('Sponsoring collection from evm address via access list', async ({helper}) => {108 const owner = await helper.eth.createAccountWithBalance(donor);107 const owner = await helper.eth.createAccountWithBalance(donor);108109 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);109 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', '');110110111 let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});112 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);113 const collectionId = helper.ethAddress.extractCollectionId(collectionIdAddress);114 const collection = helper.nft.getCollectionObject(collectionId);111 const collection = helper.nft.getCollectionObject(collectionId);115 const sponsor = await helper.eth.createAccountWithBalance(donor);112 const sponsor = await helper.eth.createAccountWithBalance(donor);116 const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner);113 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);117114118 result = await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner});115 await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner});119 let collectionData = (await collection.getData())!;116 let collectionData = (await collection.getData())!;120 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));117 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));121 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');118 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');144 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));141 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));145142146 {143 {147 const nextTokenId = await collectionEvm.methods.nextTokenId().call();148 expect(nextTokenId).to.be.equal('1');149 const result = await collectionEvm.methods.mintWithTokenURI(144 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});150 user,151 nextTokenId,152 'Test URI',153 ).send({from: user});154 const events = helper.eth.normalizeEvents(result.events);145 const events = helper.eth.normalizeEvents(result.events);155146156 expect(events).to.be.deep.equal([147 expect(events).to.be.deep.equal([157 {148 {158 address: collectionIdAddress,149 address: collectionAddress,159 event: 'Transfer',150 event: 'Transfer',160 args: {151 args: {161 from: '0x0000000000000000000000000000000000000000',152 from: '0x0000000000000000000000000000000000000000',162 to: user,153 to: user,163 tokenId: nextTokenId,154 tokenId: '1',164 },155 },165 },156 },166 ]);157 ]);178 // itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => {169 // itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => {179 // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);170 // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);180 // const collectionHelpers = evmCollectionHelpers(web3, owner);171 // const collectionHelpers = evmCollectionHelpers(web3, owner);181 // const result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();172 // const result = await collectionHelpers.methods.createERC721MetadataCompatibleNFTCollection('Sponsor collection', '1', '1', '').send();182 // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);173 // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);183 // const sponsor = privateKeyWrapper('//Alice');174 // const sponsor = privateKeyWrapper('//Alice');184 // const collectionEvm = evmCollection(web3, owner, collectionIdAddress);175 // const collectionEvm = evmCollection(web3, owner, collectionIdAddress);233 itEth('Check that transaction via EVM spend money from sponsor address', async ({helper}) => {224 itEth('Check that transaction via EVM spend money from sponsor address', async ({helper}) => {234 const owner = await helper.eth.createAccountWithBalance(donor);225 const owner = await helper.eth.createAccountWithBalance(donor);226235 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);227 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');236237 let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});238 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);239 const collectionId = helper.ethAddress.extractCollectionId(collectionIdAddress);240 const collection = helper.nft.getCollectionObject(collectionId);228 const collection = helper.nft.getCollectionObject(collectionId);241 const sponsor = await helper.eth.createAccountWithBalance(donor);229 const sponsor = await helper.eth.createAccountWithBalance(donor);242 const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner);230 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);243231244 result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();232 await collectionEvm.methods.setCollectionSponsor(sponsor).send();245 let collectionData = (await collection.getData())!;233 let collectionData = (await collection.getData())!;246 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));234 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));247 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');235 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');248236249 const sponsorCollection = helper.ethNativeContract.collection(collectionIdAddress, 'nft', sponsor);237 const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);250 await sponsorCollection.methods.confirmCollectionSponsorship().send();238 await sponsorCollection.methods.confirmCollectionSponsorship().send();251 collectionData = (await collection.getData())!;239 collectionData = (await collection.getData())!;252 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));240 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));257 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));245 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));258 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));246 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));259247260 const userCollectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', user);248 const userCollectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', user);249261 const nextTokenId = await userCollectionEvm.methods.nextTokenId().call();250 let result = await userCollectionEvm.methods.mintWithTokenURI(user, 'Test URI',).send();262 expect(nextTokenId).to.be.equal('1');263 result = await userCollectionEvm.methods.mintWithTokenURI(251 const tokenId = result.events.Transfer.returnValues.tokenId;264 user,265 nextTokenId,266 'Test URI',267 ).send();268252269 const events = helper.eth.normalizeEvents(result.events);253 const events = helper.eth.normalizeEvents(result.events);270 const address = helper.ethAddress.fromCollectionId(collectionId);254 const address = helper.ethAddress.fromCollectionId(collectionId);276 args: {260 args: {277 from: '0x0000000000000000000000000000000000000000',261 from: '0x0000000000000000000000000000000000000000',278 to: user,262 to: user,279 tokenId: nextTokenId,263 tokenId: '1',280 },264 },281 },265 },282 ]);266 ]);283 expect(await userCollectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');267 expect(await userCollectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');284 268285 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));269 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));286 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);270 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);tests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth35 const description = 'Some description';35 const description = 'Some description';36 const prefix = 'token prefix';36 const prefix = 'token prefix';373738 const {collectionId} = await helper.eth.createNonfungibleCollection(owner, name, description, prefix);38 const {collectionId} = await helper.eth.createNFTCollection(owner, name, description, prefix);39 const data = (await helper.rft.getData(collectionId))!;39 const data = (await helper.rft.getData(collectionId))!;40 40 const collection = helper.nft.getCollectionObject(collectionId);41 41 expect(data.name).to.be.eq(name);42 expect(data.name).to.be.eq(name);42 expect(data.description).to.be.eq(description);43 expect(data.description).to.be.eq(description);43 expect(data.raw.tokenPrefix).to.be.eq(prefix);44 expect(data.raw.tokenPrefix).to.be.eq(prefix);44 expect(data.raw.mode).to.be.eq('NFT');45 expect(data.raw.mode).to.be.eq('NFT');4647 const options = await collection.getOptions();4849 expect(options.tokenPropertyPermissions).to.be.empty;45 });50 });5152 itEth('Create collection with properties', async ({helper}) => {53 const owner = await helper.eth.createAccountWithBalance(donor);5455 const name = 'CollectionEVM';56 const description = 'Some description';57 const prefix = 'token prefix';58 const baseUri = 'BaseURI';5960 const {collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri);6162 const collection = helper.nft.getCollectionObject(collectionId);63 const data = (await collection.getData())!;64 65 expect(data.name).to.be.eq(name);66 expect(data.description).to.be.eq(description);67 expect(data.raw.tokenPrefix).to.be.eq(prefix);68 expect(data.raw.mode).to.be.eq('NFT');6970 const options = await collection.getOptions();71 expect(options.tokenPropertyPermissions).to.be.deep.equal([72 {73 key: 'URI',74 permission: {mutable: true, collectionAdmin: true, tokenOwner: false},75 },76 {77 key: 'URISuffix',78 permission: {mutable: true, collectionAdmin: true, tokenOwner: false},79 },80 ]);81 });468247 // this test will occasionally fail when in async environment.83 // this test will occasionally fail when in async environment.48 itEth.skip('Check collection address exist', async ({helper}) => {84 itEth.skip('Check collection address exist', async ({helper}) => {57 .call()).to.be.false;93 .call()).to.be.false;589459 await collectionHelpers.methods95 await collectionHelpers.methods60 .createNonfungibleCollection('A', 'A', 'A')96 .createNFTCollection('A', 'A', 'A')61 .send({value: Number(2n * helper.balance.getOneTokenNominal())});97 .send({value: Number(2n * helper.balance.getOneTokenNominal())});62 98 63 expect(await collectionHelpers.methods99 expect(await collectionHelpers.methods69 const owner = await helper.eth.createAccountWithBalance(donor);105 const owner = await helper.eth.createAccountWithBalance(donor);70 const sponsor = await helper.eth.createAccountWithBalance(donor);106 const sponsor = await helper.eth.createAccountWithBalance(donor);71 const ss58Format = helper.chain.getChainProperties().ss58Format;107 const ss58Format = helper.chain.getChainProperties().ss58Format;72 const {collectionId, collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');108 const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');7310974 const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);110 const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);75 await collection.methods.setCollectionSponsor(sponsor).send();111 await collection.methods.setCollectionSponsor(sponsor).send();8812489 itEth('Set limits', async ({helper}) => {125 itEth('Set limits', async ({helper}) => {90 const owner = await helper.eth.createAccountWithBalance(donor);126 const owner = await helper.eth.createAccountWithBalance(donor);91 const {collectionId, collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Limits', 'absolutely anything', 'FLO');127 const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'FLO');92 const limits = {128 const limits = {93 accountTokenOwnershipLimit: 1000,129 accountTokenOwnershipLimit: 1000,94 sponsoredDataSize: 1024,130 sponsoredDataSize: 1024,131 .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())167 .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())132 .to.be.false;168 .to.be.false;133 169 134 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Exister', 'absolutely anything', 'EVC');170 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Exister', 'absolutely anything', 'EVC');135 expect(await helper.ethNativeContract.collectionHelpers(collectionAddress)171 expect(await helper.ethNativeContract.collectionHelpers(collectionAddress)136 .methods.isCollectionExist(collectionAddress).call())172 .methods.isCollectionExist(collectionAddress).call())137 .to.be.true;173 .to.be.true;159 const tokenPrefix = 'A';195 const tokenPrefix = 'A';160196161 await expect(collectionHelper.methods197 await expect(collectionHelper.methods162 .createNonfungibleCollection(collectionName, description, tokenPrefix)198 .createNFTCollection(collectionName, description, tokenPrefix)163 .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);199 .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);164 200 165 }201 }169 const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);205 const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);170 const tokenPrefix = 'A';206 const tokenPrefix = 'A';171 await expect(collectionHelper.methods207 await expect(collectionHelper.methods172 .createNonfungibleCollection(collectionName, description, tokenPrefix)208 .createNFTCollection(collectionName, description, tokenPrefix)173 .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);209 .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);174 }210 }175 {211 {178 const description = 'A';214 const description = 'A';179 const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);215 const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);180 await expect(collectionHelper.methods216 await expect(collectionHelper.methods181 .createNonfungibleCollection(collectionName, description, tokenPrefix)217 .createNFTCollection(collectionName, description, tokenPrefix)182 .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);218 .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);183 }219 }184 });220 });187 const owner = await helper.eth.createAccountWithBalance(donor);223 const owner = await helper.eth.createAccountWithBalance(donor);188 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);224 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);189 await expect(collectionHelper.methods225 await expect(collectionHelper.methods190 .createNonfungibleCollection('Peasantry', 'absolutely anything', 'CVE')226 .createNFTCollection('Peasantry', 'absolutely anything', 'CVE')191 .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');227 .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');192 });228 });193229194 itEth('(!negative test!) Check owner', async ({helper}) => {230 itEth('(!negative test!) Check owner', async ({helper}) => {195 const owner = await helper.eth.createAccountWithBalance(donor);231 const owner = await helper.eth.createAccountWithBalance(donor);196 const malfeasant = helper.eth.createAccount();232 const malfeasant = helper.eth.createAccount();197 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Transgressed', 'absolutely anything', 'COR');233 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR');198 const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);234 const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);199 const EXPECTED_ERROR = 'NoPermission';235 const EXPECTED_ERROR = 'NoPermission';200 {236 {217253218 itEth('(!negative test!) Set limits', async ({helper}) => {254 itEth('(!negative test!) Set limits', async ({helper}) => {219 const owner = await helper.eth.createAccountWithBalance(donor);255 const owner = await helper.eth.createAccountWithBalance(donor);220 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Limits', 'absolutely anything', 'OLF');256 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');221 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);257 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);222 await expect(collectionEvm.methods258 await expect(collectionEvm.methods223 .setCollectionLimit('badLimit', 'true')259 .setCollectionLimit('badLimit', 'true')tests/src/eth/createRFTCollection.test.tsdiffbeforeafterboth37 const description = 'Some description';37 const description = 'Some description';38 const prefix = 'token prefix';38 const prefix = 'token prefix';39 39 40 const {collectionId} = await helper.eth.createRefungibleCollection(owner, name, description, prefix);40 const {collectionId} = await helper.eth.createRFTCollection(owner, name, description, prefix);41 const data = (await helper.rft.getData(collectionId))!;41 const data = (await helper.rft.getData(collectionId))!;42 const collection = helper.rft.getCollectionObject(collectionId);424343 expect(data.name).to.be.eq(name);44 expect(data.name).to.be.eq(name);44 expect(data.description).to.be.eq(description);45 expect(data.description).to.be.eq(description);45 expect(data.raw.tokenPrefix).to.be.eq(prefix);46 expect(data.raw.tokenPrefix).to.be.eq(prefix);46 expect(data.raw.mode).to.be.eq('ReFungible');47 expect(data.raw.mode).to.be.eq('ReFungible');4849 const options = await collection.getOptions();5051 expect(options.tokenPropertyPermissions).to.be.empty;47 });52 });48 5354 5556 itEth('Create collection with properties', async ({helper}) => {57 const owner = await helper.eth.createAccountWithBalance(donor);5859 const name = 'CollectionEVM';60 const description = 'Some description';61 const prefix = 'token prefix';62 const baseUri = 'BaseURI';6364 const {collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri);6566 const collection = helper.rft.getCollectionObject(collectionId);67 const data = (await collection.getData())!;68 69 expect(data.name).to.be.eq(name);70 expect(data.description).to.be.eq(description);71 expect(data.raw.tokenPrefix).to.be.eq(prefix);72 expect(data.raw.mode).to.be.eq('ReFungible');7374 const options = await collection.getOptions();75 expect(options.tokenPropertyPermissions).to.be.deep.equal([76 {77 key: 'URI',78 permission: {mutable: true, collectionAdmin: true, tokenOwner: false},79 },80 {81 key: 'URISuffix',82 permission: {mutable: true, collectionAdmin: true, tokenOwner: false},83 },84 ]);85 });86 49 // this test will occasionally fail when in async environment.87 // this test will occasionally fail when in async environment.50 itEth.skip('Check collection address exist', async ({helper}) => {88 itEth.skip('Check collection address exist', async ({helper}) => {71 const owner = await helper.eth.createAccountWithBalance(donor);109 const owner = await helper.eth.createAccountWithBalance(donor);72 const sponsor = await helper.eth.createAccountWithBalance(donor);110 const sponsor = await helper.eth.createAccountWithBalance(donor);73 const ss58Format = helper.chain.getChainProperties().ss58Format;111 const ss58Format = helper.chain.getChainProperties().ss58Format;74 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');112 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');7511376 const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);114 const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);77 await collection.methods.setCollectionSponsor(sponsor).send();115 await collection.methods.setCollectionSponsor(sponsor).send();9012891 itEth('Set limits', async ({helper}) => {129 itEth('Set limits', async ({helper}) => {92 const owner = await helper.eth.createAccountWithBalance(donor);130 const owner = await helper.eth.createAccountWithBalance(donor);93 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Limits', 'absolutely anything', 'INSI');131 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'INSI');94 const limits = {132 const limits = {95 accountTokenOwnershipLimit: 1000,133 accountTokenOwnershipLimit: 1000,96 sponsoredDataSize: 1024,134 sponsoredDataSize: 1024,133 .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())171 .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())134 .to.be.false;172 .to.be.false;135 173 136 const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Exister', 'absolutely anything', 'WIWT');174 const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Exister', 'absolutely anything', 'WIWT');137 expect(await helper.ethNativeContract.collectionHelpers(collectionAddress)175 expect(await helper.ethNativeContract.collectionHelpers(collectionAddress)138 .methods.isCollectionExist(collectionAddress).call())176 .methods.isCollectionExist(collectionAddress).call())139 .to.be.true;177 .to.be.true;196 itEth('(!negative test!) Check owner', async ({helper}) => {234 itEth('(!negative test!) Check owner', async ({helper}) => {197 const owner = await helper.eth.createAccountWithBalance(donor);235 const owner = await helper.eth.createAccountWithBalance(donor);198 const peasant = helper.eth.createAccount();236 const peasant = helper.eth.createAccount();199 const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');237 const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');200 const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);238 const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);201 const EXPECTED_ERROR = 'NoPermission';239 const EXPECTED_ERROR = 'NoPermission';202 {240 {219257220 itEth('(!negative test!) Set limits', async ({helper}) => {258 itEth('(!negative test!) Set limits', async ({helper}) => {221 const owner = await helper.eth.createAccountWithBalance(donor);259 const owner = await helper.eth.createAccountWithBalance(donor);222 const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Limits', 'absolutely anything', 'ISNI');260 const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'ISNI');223 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);261 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);224 await expect(collectionEvm.methods262 await expect(collectionEvm.methods225 .setCollectionLimit('badLimit', 'true')263 .setCollectionLimit('badLimit', 'true')tests/src/eth/evmCoder.test.tsdiffbeforeafterboth65 65 66 itEth('Call non-existing function', async ({helper}) => {66 itEth('Call non-existing function', async ({helper}) => {67 const owner = await helper.eth.createAccountWithBalance(donor);67 const owner = await helper.eth.createAccountWithBalance(donor);68 const collection = await helper.eth.createNonfungibleCollection(owner, 'EVMCODER', '', 'TEST');68 const collection = await helper.eth.createNFTCollection(owner, 'EVMCODER', '', 'TEST');69 const contract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, '0x1bfed5D614b886b9Ab2eA4CBAc22A96B7EC29c9c'));69 const contract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, '0x1bfed5D614b886b9Ab2eA4CBAc22A96B7EC29c9c'));70 const testContract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, contract.options.address));70 const testContract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, contract.options.address));71 {71 {tests/src/eth/fractionalizer/Fractionalizer.soldiffbeforeafterboth124 address rftTokenAddress;124 address rftTokenAddress;125 UniqueRefungibleToken rftTokenContract;125 UniqueRefungibleToken rftTokenContract;126 if (nft2rftMapping[_collection][_token] == 0) {126 if (nft2rftMapping[_collection][_token] == 0) {127 rftTokenId = rftCollectionContract.nextTokenId();128 rftCollectionContract.mint(address(this), rftTokenId);127 rftTokenId = rftCollectionContract.mint(address(this));129 rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId);128 rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId);130 nft2rftMapping[_collection][_token] = rftTokenId;129 nft2rftMapping[_collection][_token] = rftTokenId;131 rft2nftMapping[rftTokenAddress] = Token(_collection, _token);130 rft2nftMapping[rftTokenAddress] = Token(_collection, _token);tests/src/eth/fractionalizer/fractionalizer.test.tsdiffbeforeafterboth62const mintRFTToken = async (helper: EthUniqueHelper, owner: string, fractionalizer: Contract, amount: bigint): Promise<{62const mintRFTToken = async (helper: EthUniqueHelper, owner: string, fractionalizer: Contract, amount: bigint): Promise<{63 nftCollectionAddress: string, nftTokenId: number, rftTokenAddress: string63 nftCollectionAddress: string, nftTokenId: number, rftTokenAddress: string64}> => {64}> => {65 const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');65 const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');66 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);66 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);67 const nftTokenId = await nftContract.methods.nextTokenId().call();67 const mintResult = await nftContract.methods.mint(owner).send({from: owner});68 await nftContract.methods.mint(owner, nftTokenId).send({from: owner});68 const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;696970 await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});70 await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});71 await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});71 await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});92 itEth('Set RFT collection', async ({helper}) => {92 itEth('Set RFT collection', async ({helper}) => {93 const owner = await helper.eth.createAccountWithBalance(donor, 10n);93 const owner = await helper.eth.createAccountWithBalance(donor, 10n);94 const fractionalizer = await deployContract(helper, owner);94 const fractionalizer = await deployContract(helper, owner);95 const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');95 const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');96 const rftContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);96 const rftContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);979798 await rftContract.methods.addCollectionAdmin(fractionalizer.options.address).send({from: owner});98 await rftContract.methods.addCollectionAdmin(fractionalizer.options.address).send({from: owner});121 itEth('Set Allowlist', async ({helper}) => {121 itEth('Set Allowlist', async ({helper}) => {122 const owner = await helper.eth.createAccountWithBalance(donor, 20n);122 const owner = await helper.eth.createAccountWithBalance(donor, 20n);123 const {contract: fractionalizer} = await initContract(helper, owner);123 const {contract: fractionalizer} = await initContract(helper, owner);124 const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');124 const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');125125126 const result1 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});126 const result1 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});127 expect(result1.events).to.be.like({127 expect(result1.events).to.be.like({146 itEth('NFT to RFT', async ({helper}) => {146 itEth('NFT to RFT', async ({helper}) => {147 const owner = await helper.eth.createAccountWithBalance(donor, 20n);147 const owner = await helper.eth.createAccountWithBalance(donor, 20n);148148149 const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');149 const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');150 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);150 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);151 const nftTokenId = await nftContract.methods.nextTokenId().call();151 const mintResult = await nftContract.methods.mint(owner).send({from: owner});152 await nftContract.methods.mint(owner, nftTokenId).send({from: owner});152 const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;153153154 const {contract: fractionalizer} = await initContract(helper, owner);154 const {contract: fractionalizer} = await initContract(helper, owner);155155231231232 itEth('call setRFTCollection twice', async ({helper}) => {232 itEth('call setRFTCollection twice', async ({helper}) => {233 const owner = await helper.eth.createAccountWithBalance(donor, 20n);233 const owner = await helper.eth.createAccountWithBalance(donor, 20n);234 const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');234 const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');235 const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);235 const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);236236237 const fractionalizer = await deployContract(helper, owner);237 const fractionalizer = await deployContract(helper, owner);244244245 itEth('call setRFTCollection with NFT collection', async ({helper}) => {245 itEth('call setRFTCollection with NFT collection', async ({helper}) => {246 const owner = await helper.eth.createAccountWithBalance(donor, 20n);246 const owner = await helper.eth.createAccountWithBalance(donor, 20n);247 const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');247 const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');248 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);248 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);249249250 const fractionalizer = await deployContract(helper, owner);250 const fractionalizer = await deployContract(helper, owner);257 itEth('call setRFTCollection while not collection admin', async ({helper}) => {257 itEth('call setRFTCollection while not collection admin', async ({helper}) => {258 const owner = await helper.eth.createAccountWithBalance(donor, 20n);258 const owner = await helper.eth.createAccountWithBalance(donor, 20n);259 const fractionalizer = await deployContract(helper, owner);259 const fractionalizer = await deployContract(helper, owner);260 const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');260 const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');261261262 await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call())262 await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call())263 .to.be.rejectedWith(/Fractionalizer contract should be an admin of the collection$/g);263 .to.be.rejectedWith(/Fractionalizer contract should be an admin of the collection$/g);278 itEth('call nft2rft without setting RFT collection for contract', async ({helper}) => {278 itEth('call nft2rft without setting RFT collection for contract', async ({helper}) => {279 const owner = await helper.eth.createAccountWithBalance(donor, 20n);279 const owner = await helper.eth.createAccountWithBalance(donor, 20n);280280281 const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');281 const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');282 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);282 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);283 const nftTokenId = await nftContract.methods.nextTokenId().call();283 const mintResult = await nftContract.methods.mint(owner).send({from: owner});284 await nftContract.methods.mint(owner, nftTokenId).send({from: owner});284 const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;285285286 const fractionalizer = await deployContract(helper, owner);286 const fractionalizer = await deployContract(helper, owner);287287293 const owner = await helper.eth.createAccountWithBalance(donor, 20n);293 const owner = await helper.eth.createAccountWithBalance(donor, 20n);294 const nftOwner = await helper.eth.createAccountWithBalance(donor, 10n);294 const nftOwner = await helper.eth.createAccountWithBalance(donor, 10n);295295296 const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');296 const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');297 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);297 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);298 const nftTokenId = await nftContract.methods.nextTokenId().call();298 const mintResult = await nftContract.methods.mint(owner).send({from: owner});299 await nftContract.methods.mint(owner, nftTokenId).send({from: owner});299 const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;300 await nftContract.methods.transfer(nftOwner, 1).send({from: owner});300 await nftContract.methods.transfer(nftOwner, 1).send({from: owner});301301302302310 itEth('call nft2rft while not in list of allowed accounts', async ({helper}) => {310 itEth('call nft2rft while not in list of allowed accounts', async ({helper}) => {311 const owner = await helper.eth.createAccountWithBalance(donor, 20n);311 const owner = await helper.eth.createAccountWithBalance(donor, 20n);312312313 const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');313 const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');314 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);314 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);315 const nftTokenId = await nftContract.methods.nextTokenId().call();315 const mintResult = await nftContract.methods.mint(owner).send({from: owner});316 await nftContract.methods.mint(owner, nftTokenId).send({from: owner});316 const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;317317318 const {contract: fractionalizer} = await initContract(helper, owner);318 const {contract: fractionalizer} = await initContract(helper, owner);319319325 itEth('call nft2rft while fractionalizer doesnt have approval for nft token', async ({helper}) => {325 itEth('call nft2rft while fractionalizer doesnt have approval for nft token', async ({helper}) => {326 const owner = await helper.eth.createAccountWithBalance(donor, 20n);326 const owner = await helper.eth.createAccountWithBalance(donor, 20n);327327328 const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');328 const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');329 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);329 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);330 const nftTokenId = await nftContract.methods.nextTokenId().call();330 const mintResult = await nftContract.methods.mint(owner).send({from: owner});331 await nftContract.methods.mint(owner, nftTokenId).send({from: owner});331 const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;332332333 const {contract: fractionalizer} = await initContract(helper, owner);333 const {contract: fractionalizer} = await initContract(helper, owner);334334341 const owner = await helper.eth.createAccountWithBalance(donor, 20n);341 const owner = await helper.eth.createAccountWithBalance(donor, 20n);342342343 const fractionalizer = await deployContract(helper, owner);343 const fractionalizer = await deployContract(helper, owner);344 const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');344 const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');345 const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);345 const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);346 const rftTokenId = await refungibleContract.methods.nextTokenId().call();346 const mintResult = await refungibleContract.methods.mint(owner).send({from: owner});347 await refungibleContract.methods.mint(owner, rftTokenId).send({from: owner});347 const rftTokenId = mintResult.events.Transfer.returnValues.tokenId;348 348349 await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call({from: owner}))349 await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call({from: owner}))350 .to.be.rejectedWith(/RFT collection is not set$/g);350 .to.be.rejectedWith(/RFT collection is not set$/g);354 const owner = await helper.eth.createAccountWithBalance(donor, 20n);354 const owner = await helper.eth.createAccountWithBalance(donor, 20n);355355356 const {contract: fractionalizer} = await initContract(helper, owner);356 const {contract: fractionalizer} = await initContract(helper, owner);357 const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');357 const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');358 const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);358 const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);359 const rftTokenId = await refungibleContract.methods.nextTokenId().call();359 const mintResult = await refungibleContract.methods.mint(owner).send({from: owner});360 await refungibleContract.methods.mint(owner, rftTokenId).send({from: owner});360 const rftTokenId = mintResult.events.Transfer.returnValues.tokenId;361 361362 await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call())362 await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call())363 .to.be.rejectedWith(/Wrong RFT collection$/g);363 .to.be.rejectedWith(/Wrong RFT collection$/g);364 });364 });365365366 itEth('call rft2nft for RFT token that was not minted by fractionalizer contract', async ({helper}) => {366 itEth('call rft2nft for RFT token that was not minted by fractionalizer contract', async ({helper}) => {367 const owner = await helper.eth.createAccountWithBalance(donor, 20n);367 const owner = await helper.eth.createAccountWithBalance(donor, 20n);368 const rftCollection = await helper.eth.createRefungibleCollection(owner, 'rft', 'RFT collection', 'RFT');368 const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');369 const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);369 const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);370370371 const fractionalizer = await deployContract(helper, owner);371 const fractionalizer = await deployContract(helper, owner);372372373 await refungibleContract.methods.addCollectionAdmin(fractionalizer.options.address).send({from: owner});373 await refungibleContract.methods.addCollectionAdmin(fractionalizer.options.address).send({from: owner});374 await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner});374 await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner});375375376 const rftTokenId = await refungibleContract.methods.nextTokenId().call();376 const mintResult = await refungibleContract.methods.mint(owner).send({from: owner});377 await refungibleContract.methods.mint(owner, rftTokenId).send({from: owner});377 const rftTokenId = mintResult.events.Transfer.returnValues.tokenId;378 378379 await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call())379 await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call())380 .to.be.rejectedWith(/No corresponding NFT token found$/g);380 .to.be.rejectedWith(/No corresponding NFT token found$/g);432 await fractionalizer.methods.setRFTCollection(rftCollectionAddress).send({from: owner});432 await fractionalizer.methods.setRFTCollection(rftCollectionAddress).send({from: owner});433 await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [rftCollection.collectionId, false], true);433 await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [rftCollection.collectionId, false], true);434434435 const nftCollection = await helper.eth.createNonfungibleCollection(owner, 'nft', 'NFT collection', 'NFT');435 const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');436 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);436 const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);437 const nftTokenId = await nftContract.methods.nextTokenId().call();437 const mintResult = await nftContract.methods.mint(owner).send({from: owner});438 await nftContract.methods.mint(owner, nftTokenId).send({from: owner});438 const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;439439440 await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});440 await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});441 await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});441 await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});tests/src/eth/fungibleAbi.jsondiffbeforeafterboth115 "stateMutability": "nonpayable",115 "stateMutability": "nonpayable",116 "type": "function"116 "type": "function"117 },117 },118 {119 "inputs": [120 { "internalType": "address", "name": "newOwner", "type": "address" }121 ],122 "name": "changeCollectionOwner",123 "outputs": [],124 "stateMutability": "nonpayable",125 "type": "function"126 },118 {127 {119 "inputs": [],128 "inputs": [],120 "name": "collectionOwner",129 "name": "collectionOwner",333 "stateMutability": "nonpayable",342 "stateMutability": "nonpayable",334 "type": "function"343 "type": "function"335 },344 },336 {337 "inputs": [338 { "internalType": "address", "name": "newOwner", "type": "address" }339 ],340 "name": "setOwner",341 "outputs": [],342 "stateMutability": "nonpayable",343 "type": "function"344 },345 {345 {346 "inputs": [346 "inputs": [347 { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }347 { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }tests/src/eth/nesting/nest.test.tsdiffbeforeafterboth7 helper: EthUniqueHelper,7 helper: EthUniqueHelper,8 owner: string,8 owner: string,9): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {9): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {10 const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');10 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');111112 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);12 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);13 await contract.methods.setCollectionNesting(true).send({from: owner});13 await contract.methods.setCollectionNesting(true).send({from: owner});30 const owner = await helper.eth.createAccountWithBalance(donor);30 const owner = await helper.eth.createAccountWithBalance(donor);31 const {collectionId, contract} = await createNestingCollection(helper, owner);31 const {collectionId, contract} = await createNestingCollection(helper, owner);32 3233 // Create a token to be nested33 // Create a token to be nested to34 const targetNFTTokenId = await contract.methods.nextTokenId().call();34 const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});35 await contract.methods.mint(35 const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;36 owner,37 targetNFTTokenId,38 ).send({from: owner});39 40 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);36 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);41 3742 // Create a nested token38 // Create a nested token43 const firstTokenId = await contract.methods.nextTokenId().call();39 const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});44 await contract.methods.mint(40 const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;45 targetNftTokenAddress,46 firstTokenId,47 ).send({from: owner});48 49 expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);41 expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);50 4251 // Create a token to be nested and nest43 // Create a token to be nested and nest52 const secondTokenId = await contract.methods.nextTokenId().call();44 const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});53 await contract.methods.mint(45 const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;54 owner,4655 secondTokenId,56 ).send({from: owner});57 58 await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});47 await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});59 72 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});60 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});73 6174 // Create a token to nest into62 // Create a token to nest into75 const targetNftTokenId = await contractA.methods.nextTokenId().call();63 const mintingtargetNftTokenIdResult = await contractA.methods.mint(owner).send({from: owner});76 await contractA.methods.mint(64 const targetNftTokenId = mintingtargetNftTokenIdResult.events.Transfer.returnValues.tokenId;77 owner,78 targetNftTokenId,79 ).send({from: owner});80 const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId);65 const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId);81 6682 // Create a token for nesting in the same collection as the target67 // Create a token for nesting in the same collection as the target83 const nftTokenIdA = await contractA.methods.nextTokenId().call();68 const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});84 await contractA.methods.mint(85 owner,86 nftTokenIdA,87 ).send({from: owner});88 89 // Create a token for nesting in a different collection90 const nftTokenIdB = await contractB.methods.nextTokenId().call();69 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;7071 // Create a token for nesting in a different collection91 await contractB.methods.mint(72 const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});92 owner,73 const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;93 nftTokenIdB,7494 ).send({from: owner});95 96 // Nest75 // Nest110 await contract.methods.setCollectionNesting(false).send({from: owner});89 await contract.methods.setCollectionNesting(false).send({from: owner});111 90112 // Create a token to nest into91 // Create a token to nest into113 const targetNftTokenId = await contract.methods.nextTokenId().call();92 const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});114 await contract.methods.mint(93 const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;115 owner,116 targetNftTokenId,117 ).send({from: owner});118 119 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNftTokenId);94 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);120 95121 // Create a token to nest96 // Create a token to nest122 const nftTokenId = await contract.methods.nextTokenId().call();97 const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});123 await contract.methods.mint(98 const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;124 owner,99125 nftTokenId,126 ).send({from: owner});127 128 // Try to nest100 // Try to nest129 await expect(contract.methods101 await expect(contract.methods138 const {collectionId, contract} = await createNestingCollection(helper, owner);110 const {collectionId, contract} = await createNestingCollection(helper, owner);139 111140 // Mint a token112 // Mint a token141 const targetTokenId = await contract.methods.nextTokenId().call();113 const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});142 await contract.methods.mint(114 const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;143 owner,144 targetTokenId,145 ).send({from: owner});146 const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);115 const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);147 116148 // Mint a token belonging to a different account117 // Mint a token belonging to a different account149 const tokenId = await contract.methods.nextTokenId().call();118 const mintingTokenIdResult = await contract.methods.mint(malignant).send({from: owner});150 await contract.methods.mint(119 const tokenId = mintingTokenIdResult.events.Transfer.returnValues.tokenId;151 malignant,120152 tokenId,153 ).send({from: owner});154 155 // Try to nest one token in another as a non-owner account121 // Try to nest one token in another as a non-owner account156 await expect(contract.methods122 await expect(contract.methods168 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});134 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});169 135170 // Create a token in one collection136 // Create a token in one collection171 const nftTokenIdA = await contractA.methods.nextTokenId().call();137 const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});172 await contractA.methods.mint(138 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;173 owner,174 nftTokenIdA,175 ).send({from: owner});176 const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);139 const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);177 140178 // Create a token in another collection belonging to someone else141 // Create a token in another collection179 const nftTokenIdB = await contractB.methods.nextTokenId().call();142 const mintingTokenIdBResult = await contractB.methods.mint(malignant).send({from: owner});180 await contractB.methods.mint(143 const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;181 malignant,144182 nftTokenIdB,183 ).send({from: owner});184 185 // Try to drag someone else's token into the other collection and nest145 // Try to drag someone else's token into the other collection and nest186 await expect(contractB.methods146 await expect(contractB.methods197 await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner});157 await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner});198 158199 // Create a token in one collection159 // Create a token in one collection200 const nftTokenIdA = await contractA.methods.nextTokenId().call();160 const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});201 await contractA.methods.mint(161 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;202 owner,203 nftTokenIdA,204 ).send({from: owner});205 const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);162 const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);206 163207 // Create a token in another collection164 // Create a token in another collection208 const nftTokenIdB = await contractB.methods.nextTokenId().call();165 const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});209 await contractB.methods.mint(166 const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;210 owner,167211 nftTokenIdB,168212 ).send({from: owner});213 214 // Try to nest into a token in the other collection, disallowed in the first169 // Try to nest into a token in the other collection, disallowed in the first215 await expect(contractB.methods170 await expect(contractB.methodstests/src/eth/nonFungible.test.tsdiffbeforeafterboth69 expect(owner).to.equal(caller);69 expect(owner).to.equal(caller);70 });70 });7172 itEth('name/symbol is available regardless of ERC721Metadata support', async ({helper}) => {73 const collection = await helper.nft.mintCollection(alice, {name: 'test', tokenPrefix: 'TEST'});74 const caller = helper.eth.createAccount();7576 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);7778 expect(await contract.methods.name().call()).to.equal('test');79 expect(await contract.methods.symbol().call()).to.equal('TEST');80 });71});81});728273describe('Check ERC721 token URI for NFT', () => {83describe('Check ERC721 token URI for NFT', () => {79 });89 });80 });90 });819182 async function setup(helper: EthUniqueHelper, tokenPrefix: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {92 async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {83 const owner = await helper.eth.createAccountWithBalance(donor);93 const owner = await helper.eth.createAccountWithBalance(donor);84 const receiver = helper.eth.createAccount();94 const receiver = helper.eth.createAccount();859586 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);96 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);87 let result = await collectionHelper.methods.createERC721MetadataCompatibleCollection('Mint collection', 'a', 'b', tokenPrefix).send({value: Number(2n * helper.balance.getOneTokenNominal())});88 const collectionAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);89 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);97 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);90 9891 const nextTokenId = await contract.methods.nextTokenId().call();99 const result = await contract.methods.mint(receiver).send();92 expect(nextTokenId).to.be.equal('1');100 const tokenId = result.events.Transfer.returnValues.tokenId;93 result = await contract.methods.mint(101 expect(tokenId).to.be.equal('1');94 receiver,95 nextTokenId,96 ).send();9710298 if (propertyKey && propertyValue) {103 if (propertyKey && propertyValue) {99 // Set URL or suffix104 // Set URL or suffix100 await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();105 await contract.methods.setProperty(tokenId, propertyKey, Buffer.from(propertyValue)).send();101 }106 }102107103 const event = result.events.Transfer;108 const event = result.events.Transfer;104 expect(event.address).to.be.equal(collectionAddress);109 expect(event.address).to.be.equal(collectionAddress);105 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');110 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');106 expect(event.returnValues.to).to.be.equal(receiver);111 expect(event.returnValues.to).to.be.equal(receiver);107 expect(event.returnValues.tokenId).to.be.equal(nextTokenId);112 expect(event.returnValues.tokenId).to.be.equal(tokenId);108113109 return {contract, nextTokenId};114 return {contract, nextTokenId: tokenId};110 }115 }111116112 itEth('Empty tokenURI', async ({helper}) => {117 itEth('Empty tokenURI', async ({helper}) => {115 });120 });116121117 itEth('TokenURI from url', async ({helper}) => {122 itEth('TokenURI from url', async ({helper}) => {118 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'url', 'Token URI');123 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');119 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');124 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');120 });125 });121126122 itEth('TokenURI from baseURI + tokenId', async ({helper}) => {127 itEth('TokenURI from baseURI', async ({helper}) => {123 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');128 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');124 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + nextTokenId);129 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');125 });130 });126131127 itEth('TokenURI from baseURI + suffix', async ({helper}) => {132 itEth('TokenURI from baseURI + suffix', async ({helper}) => {128 const suffix = '/some/suffix';133 const suffix = '/some/suffix';129 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'suffix', suffix);134 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);130 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);135 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);131 });136 });132});137});146 const owner = await helper.eth.createAccountWithBalance(donor);151 const owner = await helper.eth.createAccountWithBalance(donor);147 const receiver = helper.eth.createAccount();152 const receiver = helper.eth.createAccount();148153149 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Minty', '6', '6');154 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');150 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);155 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);156151 const nextTokenId = await contract.methods.nextTokenId().call();157 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();152153 expect(nextTokenId).to.be.equal('1');158 const tokenId = result.events.Transfer.returnValues.tokenId;154 const result = await contract.methods.mintWithTokenURI(159 expect(tokenId).to.be.equal('1');155 receiver,156 nextTokenId,157 'Test URI',158 ).send();159160160 const event = result.events.Transfer;161 const event = result.events.Transfer;161 expect(event.address).to.be.equal(collectionAddress);162 expect(event.address).to.be.equal(collectionAddress);162 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');163 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');163 expect(event.returnValues.to).to.be.equal(receiver);164 expect(event.returnValues.to).to.be.equal(receiver);164 expect(event.returnValues.tokenId).to.be.equal(nextTokenId);165165166 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');166 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');167167168 // TODO: this wont work right now, need release 919000 first168 // TODO: this wont work right now, need release 919000 first169 // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();169 // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();509511510 itEth('Returns collection name', async ({helper}) => {512 itEth('Returns collection name', async ({helper}) => {511 const caller = await helper.eth.createAccountWithBalance(donor);513 const caller = await helper.eth.createAccountWithBalance(donor);514 const tokenPropertyPermissions = [{515 key: 'URI',516 permission: {517 mutable: true,518 collectionAdmin: true,519 tokenOwner: false,520 },521 }];512 const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});522 const collection = await helper.nft.mintCollection(523 alice,524 {525 name: 'oh River',526 tokenPrefix: 'CHANGE',527 properties: [{key: 'ERC721Metadata', value: '1'}],528 tokenPropertyPermissions,529 },530 );513531514 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);532 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);518536519 itEth('Returns symbol name', async ({helper}) => {537 itEth('Returns symbol name', async ({helper}) => {520 const caller = await helper.eth.createAccountWithBalance(donor);538 const caller = await helper.eth.createAccountWithBalance(donor);539 const tokenPropertyPermissions = [{540 key: 'URI',541 permission: {542 mutable: true,543 collectionAdmin: true,544 tokenOwner: false,545 },546 }];521 const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});547 const collection = await helper.nft.mintCollection(548 alice,549 {550 name: 'oh River',551 tokenPrefix: 'CHANGE',552 properties: [{key: 'ERC721Metadata', value: '1'}],553 tokenPropertyPermissions,554 },555 );522556523 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);557 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);tests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth145 "stateMutability": "nonpayable",145 "stateMutability": "nonpayable",146 "type": "function"146 "type": "function"147 },147 },148 {149 "inputs": [150 { "internalType": "address", "name": "newOwner", "type": "address" }151 ],152 "name": "changeCollectionOwner",153 "outputs": [],154 "stateMutability": "nonpayable",155 "type": "function"156 },148 {157 {149 "inputs": [],158 "inputs": [],150 "name": "collectionOwner",159 "name": "collectionOwner",261 },270 },262 {271 {263 "inputs": [272 "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],264 { "internalType": "address", "name": "to", "type": "address" },265 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }266 ],267 "name": "mint",273 "name": "mint",268 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],274 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],269 "stateMutability": "nonpayable",275 "stateMutability": "nonpayable",270 "type": "function"276 "type": "function"271 },277 },287 { "internalType": "uint256", "name": "field_0", "type": "uint256" },293 { "internalType": "uint256", "name": "field_0", "type": "uint256" },288 { "internalType": "string", "name": "field_1", "type": "string" }294 { "internalType": "string", "name": "field_1", "type": "string" }289 ],295 ],290 "internalType": "struct Tuple8[]",296 "internalType": "struct Tuple6[]",291 "name": "tokens",297 "name": "tokens",292 "type": "tuple[]"298 "type": "tuple[]"293 }299 }300 {306 {301 "inputs": [307 "inputs": [302 { "internalType": "address", "name": "to", "type": "address" },308 { "internalType": "address", "name": "to", "type": "address" },303 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },304 { "internalType": "string", "name": "tokenUri", "type": "string" }309 { "internalType": "string", "name": "tokenUri", "type": "string" }305 ],310 ],306 "name": "mintWithTokenURI",311 "name": "mintWithTokenURI",307 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],312 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],308 "stateMutability": "nonpayable",313 "stateMutability": "nonpayable",309 "type": "function"314 "type": "function"310 },315 },480 "stateMutability": "nonpayable",485 "stateMutability": "nonpayable",481 "type": "function"486 "type": "function"482 },487 },483 {484 "inputs": [485 { "internalType": "address", "name": "newOwner", "type": "address" }486 ],487 "name": "setOwner",488 "outputs": [],489 "stateMutability": "nonpayable",490 "type": "function"491 },492 {488 {493 "inputs": [489 "inputs": [494 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },490 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },tests/src/eth/payable.test.tsdiffbeforeafterboth146 const caller = await helper.eth.createAccountWithBalance(donor);146 const caller = await helper.eth.createAccountWithBalance(donor);147 const contract = await deployProxyContract(helper, deployer);147 const contract = await deployProxyContract(helper, deployer);148148149 const collectionAddress = (await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;149 const collectionAddress = (await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;150 const initialCallerBalance = await helper.balance.getEthereum(caller);150 const initialCallerBalance = await helper.balance.getEthereum(caller);151 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);151 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);152 await contract.methods.mintNftToken(collectionAddress).send({from: caller});152 await contract.methods.mintNftToken(collectionAddress).send({from: caller});164164165 const initialCallerBalance = await helper.balance.getEthereum(caller);165 const initialCallerBalance = await helper.balance.getEthereum(caller);166 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);166 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);167 await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});167 await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});168 const finalCallerBalance = await helper.balance.getEthereum(caller);168 const finalCallerBalance = await helper.balance.getEthereum(caller);169 const finalContractBalance = await helper.balance.getEthereum(contract.options.address);169 const finalContractBalance = await helper.balance.getEthereum(contract.options.address);170 expect(finalCallerBalance < initialCallerBalance).to.be.true;170 expect(finalCallerBalance < initialCallerBalance).to.be.true;177 const caller = await helper.eth.createAccountWithBalance(donor);177 const caller = await helper.eth.createAccountWithBalance(donor);178 const collectionHelper = helper.ethNativeContract.collectionHelpers(caller);178 const collectionHelper = helper.ethNativeContract.collectionHelpers(caller);179 179180 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)');180 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)');181 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)');181 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)');182 });182 });183183184 itEth('Negative test: call createRFTCollection with wrong fee', async({helper}) => {184 itEth('Negative test: call createRFTCollection with wrong fee', async({helper}) => {200 return await helper.ethContract.deployByCode(200 return await helper.ethContract.deployByCode(201 deployer,201 deployer,202 'ProxyContract',202 'ProxyContract',203 `203 `204 // SPDX-License-Identifier: UNLICENSED204 // SPDX-License-Identifier: UNLICENSED205 pragma solidity ^0.8.6;205 pragma solidity ^0.8.6;206206207 import {CollectionHelpers} from "../api/CollectionHelpers.sol";207 import {CollectionHelpers} from "../api/CollectionHelpers.sol";208 import {UniqueNFT} from "../api/UniqueNFT.sol";208 import {UniqueNFT} from "../api/UniqueNFT.sol";209209210 error Value(uint256 value);210 error Value(uint256 value);211211212 contract ProxyContract {212 contract ProxyContract {213 bool value = false;213 bool value = false;214 address innerContract;214 address innerContract;215215216 event CollectionCreated(address collection);216 event CollectionCreated(address collection);217 event TokenMinted(uint256 tokenId);217 event TokenMinted(uint256 tokenId);218218219 receive() external payable {}219 receive() external payable {}220220221 constructor() {221 constructor() {222 innerContract = address(new InnerContract());222 innerContract = address(new InnerContract());223 }223 }224224225 function flip() public {225 function flip() public {226 value = !value;226 value = !value;227 InnerContract(innerContract).flip();227 InnerContract(innerContract).flip();228 }228 }229229230 function createNonfungibleCollection() external payable {230 function createNFTCollection() external payable {231 address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;231 address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;232 address nftCollection = CollectionHelpers(collectionHelpers).createNonfungibleCollection{value: msg.value}("A", "B", "C");232 address nftCollection = CollectionHelpers(collectionHelpers).createNFTCollection{value: msg.value}("A", "B", "C");233 emit CollectionCreated(nftCollection);233 emit CollectionCreated(nftCollection);234 }234 }235235236 function mintNftToken(address collectionAddress) external {236 function mintNftToken(address collectionAddress) external {237 UniqueNFT collection = UniqueNFT(collectionAddress);237 UniqueNFT collection = UniqueNFT(collectionAddress);238 uint256 tokenId = collection.nextTokenId();238 uint256 tokenId = collection.mint(msg.sender);239 collection.mint(msg.sender, tokenId);239 emit TokenMinted(tokenId);240 emit TokenMinted(tokenId);240 }241 }241242242 function getValue() external view returns (bool) {243 function getValue() external view returns (bool) {243 return InnerContract(innerContract).getValue();244 return InnerContract(innerContract).getValue();244 }245 }245 }246 }246247247 contract InnerContract {248 contract InnerContract {248 bool value = false;249 bool value = false;249 function flip() external {250 function flip() external {250 value = !value;251 value = !value;251 }252 }252 function getValue() external view returns (bool) {253 function getValue() external view returns (bool) {253 return value;254 return value;254 }255 }255 }256 }256 `,257 `,258 [257 [259 {258 {260 solPath: 'api/CollectionHelpers.sol',259 solPath: 'api/CollectionHelpers.sol',tests/src/eth/proxy/UniqueNFTProxy.soldiffbeforeafterboth120 return proxied.mintingFinished();120 return proxied.mintingFinished();121 }121 }122122123 function mint(address to, uint256 tokenId)123 function mint(address to)124 external124 external125 override125 override126 returns (bool)126 returns (uint256)127 {127 {128 return proxied.mint(to, tokenId);128 return proxied.mint(to);129 }129 }130130131 function mintWithTokenURI(131 function mintWithTokenURI(132 address to,132 address to,133 uint256 tokenId,134 string memory tokenUri133 string memory tokenUri135 ) external override returns (bool) {134 ) external override returns (uint256) {136 return proxied.mintWithTokenURI(to, tokenId, tokenUri);135 return proxied.mintWithTokenURI(to, tokenUri);137 }136 }138137139 function finishMinting() external override returns (bool) {138 function finishMinting() external override returns (bool) {169 return proxied.mintBulk(to, tokenIds);168 return proxied.mintBulk(to, tokenIds);170 }169 }171170172 function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)171 function mintBulkWithTokenURI(address to, Tuple6[] memory tokens)173 external172 external174 override173 override175 returns (bool)174 returns (bool)tests/src/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth101101102 itEth('Can perform mint()', async ({helper}) => {102 itEth('Can perform mint()', async ({helper}) => {103 const owner = await helper.eth.createAccountWithBalance(donor);103 const owner = await helper.eth.createAccountWithBalance(donor);104 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'A', 'A');104 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'A', 'A', '');105 const caller = await helper.eth.createAccountWithBalance(donor);105 const caller = await helper.eth.createAccountWithBalance(donor);106 const receiver = helper.eth.createAccount();106 const receiver = helper.eth.createAccount();107107111 await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();111 await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();112112113 {113 {114 const nextTokenId = await contract.methods.nextTokenId().call();114 const nextTokenId = await contract.methods.nextTokenId().call()115 expect(nextTokenId).to.be.equal('1');116 const result = await contract.methods.mintWithTokenURI(115 const result = await contract.methods.mintWithTokenURI(receiver, nextTokenId, 'Test URI').send({from: caller});117 receiver,116 const tokenId = result.events.Transfer.returnValues.tokenId;118 nextTokenId,117 expect(tokenId).to.be.equal('1');119 'Test URI',118120 ).send({from: caller});121 const events = helper.eth.normalizeEvents(result.events);119 const events = helper.eth.normalizeEvents(result.events);128 args: {126 args: {129 from: '0x0000000000000000000000000000000000000000',127 from: '0x0000000000000000000000000000000000000000',130 to: receiver,128 to: receiver,131 tokenId: nextTokenId,129 tokenId,132 },130 },133 },131 },134 ]);132 ]);135133136 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');134 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');137 }135 }138 });136 });139137tests/src/eth/reFungible.test.tsdiffbeforeafterboth313132 itEth('totalSupply', async ({helper}) => {32 itEth('totalSupply', async ({helper}) => {33 const caller = await helper.eth.createAccountWithBalance(donor);33 const caller = await helper.eth.createAccountWithBalance(donor);34 const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'TotalSupply', '6', '6');34 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'TotalSupply', '6', '6');35 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);35 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);36 const nextTokenId = await contract.methods.nextTokenId().call();3637 await contract.methods.mint(caller, nextTokenId).send();37 await contract.methods.mint(caller).send();3838 const totalSupply = await contract.methods.totalSupply().call();39 const totalSupply = await contract.methods.totalSupply().call();39 expect(totalSupply).to.equal('1');40 expect(totalSupply).to.equal('1');40 });41 });414242 itEth('balanceOf', async ({helper}) => {43 itEth('balanceOf', async ({helper}) => {43 const caller = await helper.eth.createAccountWithBalance(donor);44 const caller = await helper.eth.createAccountWithBalance(donor);44 const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'BalanceOf', '6', '6');45 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'BalanceOf', '6', '6');45 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);46 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);464747 {48 const nextTokenId = await contract.methods.nextTokenId().call();49 await contract.methods.mint(caller, nextTokenId).send();48 await contract.methods.mint(caller).send();50 }51 {52 const nextTokenId = await contract.methods.nextTokenId().call();53 await contract.methods.mint(caller, nextTokenId).send();49 await contract.methods.mint(caller).send();54 }55 {56 const nextTokenId = await contract.methods.nextTokenId().call();57 await contract.methods.mint(caller, nextTokenId).send();50 await contract.methods.mint(caller).send();58 }595160 const balance = await contract.methods.balanceOf(caller).call();52 const balance = await contract.methods.balanceOf(caller).call();61 expect(balance).to.equal('3');53 expect(balance).to.equal('3');62 });54 });635564 itEth('ownerOf', async ({helper}) => {56 itEth('ownerOf', async ({helper}) => {65 const caller = await helper.eth.createAccountWithBalance(donor);57 const caller = await helper.eth.createAccountWithBalance(donor);66 const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'OwnerOf', '6', '6');58 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf', '6', '6');67 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);59 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);686069 const tokenId = await contract.methods.nextTokenId().call();61 const result = await contract.methods.mint(caller).send();70 await contract.methods.mint(caller, tokenId).send();62 const tokenId = result.events.Transfer.returnValues.tokenId;716372 const owner = await contract.methods.ownerOf(tokenId).call();64 const owner = await contract.methods.ownerOf(tokenId).call();73 expect(owner).to.equal(caller);65 expect(owner).to.equal(caller);76 itEth('ownerOf after burn', async ({helper}) => {68 itEth('ownerOf after burn', async ({helper}) => {77 const caller = await helper.eth.createAccountWithBalance(donor);69 const caller = await helper.eth.createAccountWithBalance(donor);78 const receiver = helper.eth.createAccount();70 const receiver = helper.eth.createAccount();79 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'OwnerOf-AfterBurn', '6', '6');71 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf-AfterBurn', '6', '6');80 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);72 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);817382 const tokenId = await contract.methods.nextTokenId().call();74 const result = await contract.methods.mint(caller).send();83 await contract.methods.mint(caller, tokenId).send();75 const tokenId = result.events.Transfer.returnValues.tokenId;84 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);76 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);857786 await tokenContract.methods.repartition(2).send();78 await tokenContract.methods.repartition(2).send();95 itEth('ownerOf for partial ownership', async ({helper}) => {87 itEth('ownerOf for partial ownership', async ({helper}) => {96 const caller = await helper.eth.createAccountWithBalance(donor);88 const caller = await helper.eth.createAccountWithBalance(donor);97 const receiver = helper.eth.createAccount();89 const receiver = helper.eth.createAccount();98 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Partial-OwnerOf', '6', '6');90 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6');99 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);91 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);10092101 const tokenId = await contract.methods.nextTokenId().call();93 const result = await contract.methods.mint(caller).send();102 await contract.methods.mint(caller, tokenId).send();94 const tokenId = result.events.Transfer.returnValues.tokenId;103 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);95 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);10496105 await tokenContract.methods.repartition(2).send();97 await tokenContract.methods.repartition(2).send();124 itEth('Can perform mint()', async ({helper}) => {116 itEth('Can perform mint()', async ({helper}) => {125 const owner = await helper.eth.createAccountWithBalance(donor);117 const owner = await helper.eth.createAccountWithBalance(donor);126 const receiver = helper.eth.createAccount();118 const receiver = helper.eth.createAccount();127 const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Minty', '6', '6');119 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Minty', '6', '6', '');128 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);120 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);129 121130 const nextTokenId = await contract.methods.nextTokenId().call();131 expect(nextTokenId).to.be.equal('1');132 const result = await contract.methods.mintWithTokenURI(122 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();133 receiver,134 nextTokenId,135 'Test URI',136 ).send();137123138 const event = result.events.Transfer;124 const event = result.events.Transfer;139 expect(event.address).to.equal(collectionAddress);125 expect(event.address).to.equal(collectionAddress);140 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');126 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');141 expect(event.returnValues.to).to.equal(receiver);127 expect(event.returnValues.to).to.equal(receiver);128 const tokenId = event.returnValues.tokenId;142 expect(event.returnValues.tokenId).to.equal(nextTokenId);129 expect(tokenId).to.be.equal('1');143130144 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');131 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');145 });132 });146133147 itEth('Can perform mintBulk()', async ({helper}) => {134 itEth('Can perform mintBulk()', async ({helper}) => {148 const owner = await helper.eth.createAccountWithBalance(donor);135 const owner = await helper.eth.createAccountWithBalance(donor);149 const receiver = helper.eth.createAccount();136 const receiver = helper.eth.createAccount();150 const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'MintBulky', '6', '6');137 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'MintBulky', '6', '6', '');151 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);138 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);152139153 {140 {179166180 itEth('Can perform burn()', async ({helper}) => {167 itEth('Can perform burn()', async ({helper}) => {181 const caller = await helper.eth.createAccountWithBalance(donor);168 const caller = await helper.eth.createAccountWithBalance(donor);182 const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Burny', '6', '6');169 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6');183 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);170 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);184171185 const tokenId = await contract.methods.nextTokenId().call();172 const result = await contract.methods.mint(caller).send();186 await contract.methods.mint(caller, tokenId).send();173 const tokenId = result.events.Transfer.returnValues.tokenId;187 {174 {188 const result = await contract.methods.burn(tokenId).send();175 const result = await contract.methods.burn(tokenId).send();189 const event = result.events.Transfer;176 const event = result.events.Transfer;197 itEth('Can perform transferFrom()', async ({helper}) => {184 itEth('Can perform transferFrom()', async ({helper}) => {198 const caller = await helper.eth.createAccountWithBalance(donor);185 const caller = await helper.eth.createAccountWithBalance(donor);199 const receiver = helper.eth.createAccount();186 const receiver = helper.eth.createAccount();200 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'TransferFromy', '6', '6');187 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6');201 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);188 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);202189203 const tokenId = await contract.methods.nextTokenId().call();190 const result = await contract.methods.mint(caller).send();191 const tokenId = result.events.Transfer.returnValues.tokenId;192204 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);193 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);205 await contract.methods.mint(caller, tokenId).send();206194207 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);195 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);208 await tokenContract.methods.repartition(15).send();196 await tokenContract.methods.repartition(15).send();241 itEth('Can perform transfer()', async ({helper}) => {229 itEth('Can perform transfer()', async ({helper}) => {242 const caller = await helper.eth.createAccountWithBalance(donor);230 const caller = await helper.eth.createAccountWithBalance(donor);243 const receiver = helper.eth.createAccount();231 const receiver = helper.eth.createAccount();244 const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry', '6', '6');232 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6');245 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);233 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);246234247 const tokenId = await contract.methods.nextTokenId().call();235 const result = await contract.methods.mint(caller).send();248 await contract.methods.mint(caller, tokenId).send();236 const tokenId = result.events.Transfer.returnValues.tokenId;249237250 {238 {251 const result = await contract.methods.transfer(receiver, tokenId).send();239 const result = await contract.methods.transfer(receiver, tokenId).send();271 itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {259 itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {272 const caller = await helper.eth.createAccountWithBalance(donor);260 const caller = await helper.eth.createAccountWithBalance(donor);273 const receiver = helper.eth.createAccount();261 const receiver = helper.eth.createAccount();274 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry-Partial-to-Full', '6', '6');262 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6');275 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);263 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);276264277 const tokenId = await contract.methods.nextTokenId().call();265 const result = await contract.methods.mint(caller).send();278 await contract.methods.mint(caller, tokenId).send();266 const tokenId = result.events.Transfer.returnValues.tokenId;279267280 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);268 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);281269300 itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {288 itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {301 const caller = await helper.eth.createAccountWithBalance(donor);289 const caller = await helper.eth.createAccountWithBalance(donor);302 const receiver = helper.eth.createAccount();290 const receiver = helper.eth.createAccount();303 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry-Full-to-Partial', '6', '6');291 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6');304 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);292 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);305293306 const tokenId = await contract.methods.nextTokenId().call();294 const result = await contract.methods.mint(caller).send();307 await contract.methods.mint(caller, tokenId).send();295 const tokenId = result.events.Transfer.returnValues.tokenId;308296309 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);297 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);310298340 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {328 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {341 const caller = await helper.eth.createAccountWithBalance(donor);329 const caller = await helper.eth.createAccountWithBalance(donor);342 const receiver = helper.eth.createAccount();330 const receiver = helper.eth.createAccount();343 const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Feeful-Transfer-From', '6', '6');331 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6');344 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);332 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);345333346 const tokenId = await contract.methods.nextTokenId().call();334 const result = await contract.methods.mint(caller).send();347 await contract.methods.mint(caller, tokenId).send();335 const tokenId = result.events.Transfer.returnValues.tokenId;348336349 const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());337 const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());350 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));338 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));354 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {342 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {355 const caller = await helper.eth.createAccountWithBalance(donor);343 const caller = await helper.eth.createAccountWithBalance(donor);356 const receiver = helper.eth.createAccount();344 const receiver = helper.eth.createAccount();357 const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Feeful-Transfer', '6', '6');345 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6');358 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);346 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);359347360 const tokenId = await contract.methods.nextTokenId().call();348 const result = await contract.methods.mint(caller).send();361 await contract.methods.mint(caller, tokenId).send();349 const tokenId = result.events.Transfer.returnValues.tokenId;362350363 const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());351 const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());364 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));352 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));381369382 itEth('Returns collection name', async ({helper}) => {370 itEth('Returns collection name', async ({helper}) => {383 const caller = helper.eth.createAccount();371 const caller = helper.eth.createAccount();372 const tokenPropertyPermissions = [{373 key: 'URI',374 permission: {375 mutable: true,376 collectionAdmin: true,377 tokenOwner: false,378 },379 }];384 const collection = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '11'});380 const collection = await helper.rft.mintCollection(385 381 alice,382 {383 name: 'Leviathan',384 tokenPrefix: '11',385 properties: [{key: 'ERC721Metadata', value: '1'}],386 tokenPropertyPermissions,387 },388 );389386 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);390 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);390394391 itEth('Returns symbol name', async ({helper}) => {395 itEth('Returns symbol name', async ({helper}) => {392 const caller = await helper.eth.createAccountWithBalance(donor);396 const caller = await helper.eth.createAccountWithBalance(donor);397 const tokenPropertyPermissions = [{398 key: 'URI',399 permission: {400 mutable: true,401 collectionAdmin: true,402 tokenOwner: false,403 },404 }];393 const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Leviathan', '', '12');405 const {collectionId} = await helper.rft.mintCollection(406 alice,407 {408 name: 'Leviathan',409 tokenPrefix: '12',410 properties: [{key: 'ERC721Metadata', value: '1'}],411 tokenPropertyPermissions,412 },413 );414394 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);415 const contract = helper.ethNativeContract.collectionById(collectionId, 'rft', caller);395 const symbol = await contract.methods.symbol().call();416 const symbol = await contract.methods.symbol().call();396 expect(symbol).to.equal('12');417 expect(symbol).to.equal('12');397 });418 });tests/src/eth/reFungibleAbi.jsondiffbeforeafterboth145 "stateMutability": "nonpayable",145 "stateMutability": "nonpayable",146 "type": "function"146 "type": "function"147 },147 },148 {149 "inputs": [150 { "internalType": "address", "name": "newOwner", "type": "address" }151 ],152 "name": "changeCollectionOwner",153 "outputs": [],154 "stateMutability": "nonpayable",155 "type": "function"156 },148 {157 {149 "inputs": [],158 "inputs": [],150 "name": "collectionOwner",159 "name": "collectionOwner",261 },270 },262 {271 {263 "inputs": [272 "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],264 { "internalType": "address", "name": "to", "type": "address" },265 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }266 ],267 "name": "mint",273 "name": "mint",268 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],274 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],269 "stateMutability": "nonpayable",275 "stateMutability": "nonpayable",270 "type": "function"276 "type": "function"271 },277 },287 { "internalType": "uint256", "name": "field_0", "type": "uint256" },293 { "internalType": "uint256", "name": "field_0", "type": "uint256" },288 { "internalType": "string", "name": "field_1", "type": "string" }294 { "internalType": "string", "name": "field_1", "type": "string" }289 ],295 ],290 "internalType": "struct Tuple8[]",296 "internalType": "struct Tuple6[]",291 "name": "tokens",297 "name": "tokens",292 "type": "tuple[]"298 "type": "tuple[]"293 }299 }300 {306 {301 "inputs": [307 "inputs": [302 { "internalType": "address", "name": "to", "type": "address" },308 { "internalType": "address", "name": "to", "type": "address" },303 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },304 { "internalType": "string", "name": "tokenUri", "type": "string" }309 { "internalType": "string", "name": "tokenUri", "type": "string" }305 ],310 ],306 "name": "mintWithTokenURI",311 "name": "mintWithTokenURI",307 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],312 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],308 "stateMutability": "nonpayable",313 "stateMutability": "nonpayable",309 "type": "function"314 "type": "function"310 },315 },480 "stateMutability": "nonpayable",485 "stateMutability": "nonpayable",481 "type": "function"486 "type": "function"482 },487 },483 {484 "inputs": [485 { "internalType": "address", "name": "newOwner", "type": "address" }486 ],487 "name": "setOwner",488 "outputs": [],489 "stateMutability": "nonpayable",490 "type": "function"491 },492 {488 {493 "inputs": [489 "inputs": [494 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },490 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },tests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth76 });76 });77 });77 });787879 async function setup(helper: EthUniqueHelper, tokenPrefix: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {79 async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {80 const owner = await helper.eth.createAccountWithBalance(donor);80 const owner = await helper.eth.createAccountWithBalance(donor);81 const receiver = helper.eth.createAccount();81 const receiver = helper.eth.createAccount();828283 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);83 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);84 let result = await collectionHelper.methods.createERC721MetadataCompatibleCollection('Mint collection', 'a', 'b', tokenPrefix).send({value: Number(2n * helper.balance.getOneTokenNominal())});85 const collectionAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);86 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);84 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);87 8588 const nextTokenId = await contract.methods.nextTokenId().call();86 const result = await contract.methods.mint(receiver).send();89 expect(nextTokenId).to.be.equal('1');90 result = await contract.methods.mint(91 receiver,92 nextTokenId,93 ).send();9495 if (propertyKey && propertyValue) {96 // Set URL or suffix97 await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();98 }9987100 const event = result.events.Transfer;88 const event = result.events.Transfer;89 const tokenId = event.returnValues.tokenId;90 expect(tokenId).to.be.equal('1');101 expect(event.address).to.be.equal(collectionAddress);91 expect(event.address).to.be.equal(collectionAddress);102 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');92 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');103 expect(event.returnValues.to).to.be.equal(receiver);93 expect(event.returnValues.to).to.be.equal(receiver);9495 if (propertyKey && propertyValue) {96 // Set URL or suffix104 expect(event.returnValues.tokenId).to.be.equal(nextTokenId);97 await contract.methods.setProperty(tokenId, propertyKey, Buffer.from(propertyValue)).send();98 }10599106 return {contract, nextTokenId};100 return {contract, nextTokenId: tokenId};107 }101 }108102109 itEth('Empty tokenURI', async ({helper}) => {103 itEth('Empty tokenURI', async ({helper}) => {112 });106 });113107114 itEth('TokenURI from url', async ({helper}) => {108 itEth('TokenURI from url', async ({helper}) => {115 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'url', 'Token URI');109 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');116 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');110 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');117 });111 });118112119 itEth('TokenURI from baseURI + tokenId', async ({helper}) => {113 itEth('TokenURI from baseURI', async ({helper}) => {120 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');114 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');121 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + nextTokenId);115 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');122 });116 });123117124 itEth('TokenURI from baseURI + suffix', async ({helper}) => {118 itEth('TokenURI from baseURI + suffix', async ({helper}) => {125 const suffix = '/some/suffix';119 const suffix = '/some/suffix';126 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'suffix', suffix);120 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);127 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);121 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);128 });122 });129});123});294 itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {288 itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {295 const caller = await helper.eth.createAccountWithBalance(donor);289 const caller = await helper.eth.createAccountWithBalance(donor);296 const receiver = await helper.eth.createAccountWithBalance(donor);290 const receiver = await helper.eth.createAccountWithBalance(donor);297 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Devastation', '6', '6');291 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Devastation', '6', '6');298 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);292 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);299293300 const tokenId = await contract.methods.nextTokenId().call();294 const result = await contract.methods.mint(caller).send();301 await contract.methods.mint(caller, tokenId).send();295 const tokenId = result.events.Transfer.returnValues.tokenId;302 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);296 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);303 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);297 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);304298484 itEth('Default parent token address and id', async ({helper}) => {478 itEth('Default parent token address and id', async ({helper}) => {485 const owner = await helper.eth.createAccountWithBalance(donor);479 const owner = await helper.eth.createAccountWithBalance(donor);486480487 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Sands', '', 'GRAIN');481 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sands', '', 'GRAIN');488 const collectionContract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);482 const collectionContract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);489 483490 const tokenId = await collectionContract.methods.nextTokenId().call();484 const result = await collectionContract.methods.mint(owner).send();491 await collectionContract.methods.mint(owner, tokenId).send();485 const tokenId = result.events.Transfer.returnValues.tokenId;486492 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);487 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);493 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);488 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth174 return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);174 return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);175 }175 }176176177 async createNonfungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {177 async createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {178 const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();178 const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();179 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);179 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);180 180181 const result = await collectionHelper.methods.createNonfungibleCollection(name, description, tokenPrefix).send({value: Number(collectionCreationPrice)});181 const result = await collectionHelper.methods.createNFTCollection(name, description, tokenPrefix).send({value: Number(collectionCreationPrice)});182182183 const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);183 const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);184 const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);184 const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);185185186 return {collectionId, collectionAddress};186 return {collectionId, collectionAddress};187 }187 }188189 async createERC721MetadataCompatibleNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{collectionId: number, collectionAddress: string}> {190 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);191192 const {collectionId, collectionAddress} = await this.createNFTCollection(signer, name, description, tokenPrefix)193194 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();195196 return {collectionId, collectionAddress};197 }188198189 async createRefungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {199 async createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {190 const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();200 const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();191 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);201 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);192 202198 return {collectionId, collectionAddress};208 return {collectionId, collectionAddress};199 }209 }210211 async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{collectionId: number, collectionAddress: string}> {212 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);213214 const {collectionId, collectionAddress} = await this.createRFTCollection(signer, name, description, tokenPrefix)215216 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();217218 return {collectionId, collectionAddress};219 }200220201 async deployCollectorContract(signer: string): Promise<Contract> {221 async deployCollectorContract(signer: string): Promise<Contract> {202 return await this.helper.ethContract.deployByCode(signer, 'Collector', `222 return await this.helper.ethContract.deployByCode(signer, 'Collector', `tests/src/util/playgrounds/unique.tsdiffbeforeafterboth1026 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1026 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1027 }1027 }10281029 async getCollectionOptions(collectionId: number) {1030 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1031 }102810321029 /**1033 /**1030 * Deletes onchain properties from the collection.1034 * Deletes onchain properties from the collection.2839 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);2843 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);2840 }2844 }28452846 async getOptions() {2847 return await this.helper.collection.getCollectionOptions(this.collectionId);2848 }284128492842 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {2850 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {2843 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);2851 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);