git.delta.rocks / unique-network / refs/commits / 2e3c4de7682f

difftreelog

Merge pull request #647 from UniqueNetwork/feature/supports-interface-for-erc721-metadata

Yaroslav Bolyukin2022-10-18parents: #764e4a6 #7e2fc0e.patch.diff
in: master

57 files changed

modified.maintain/scripts/generate_sol.shdiffbeforeafterboth
11formatted=$(mktemp)11formatted=$(mktemp)
12prettier --config $PRETTIER_CONFIG $raw > $formatted12prettier --config $PRETTIER_CONFIG $raw > $formatted
13
14sed -i -E -e "s/.+\/\/ FORMATTING: FORCE NEWLINE//g" $formatted
1315
14mv $formatted $OUTPUT16mv $formatted $OUTPUT
1517
modifiedcrates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth
291291
292struct 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 }
317
318 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);
549567
550 syn::custom_keyword!(rename_selector);568 syn::custom_keyword!(rename_selector);
569 syn::custom_keyword!(hide);
551}570}
552571
553/// Rust methods are parsed into this structure when Solidity code is generated572/// Rust methods are parsed into this structure when Solidity code is generated
558 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,
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
225225
226pub 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() == 0
248 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.selector
441 )?;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_str
448 )?;
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}
modifiedpallets/common/src/dispatch.rsdiffbeforeafterboth
9 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};
1313
14use crate::{pallet::Config, CommonCollectionOperations, CollectionHandle};14use crate::{pallet::Config, CommonCollectionOperations, CollectionHandle};
1515
80 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>;
8485
85 /// Delete the collection.86 /// Delete the collection.
modifiedpallets/common/src/erc.rsdiffbeforeafterboth
592 ///592 ///
593 /// @dev Owner can be changed only by current owner593 /// @dev Owner can be changed only by current owner
594 /// @param newOwner new owner account594 /// @param newOwner new owner account
595 #[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)?;
597598
660 pub mod key {661 pub mod key {
661 use super::*;662 use super::*;
662
663 /// Key "schemaName".
664 pub fn schema_name() -> up_data_structs::PropertyKey {
665 property_key_from_bytes(b"schemaName").expect(EXPECT_CONVERT_ERROR)
666 }
667663
668 /// Key "baseURI".664 /// Key "baseURI".
669 pub fn base_uri() -> up_data_structs::PropertyKey {665 pub fn base_uri() -> up_data_structs::PropertyKey {
672668
673 /// 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 }
677673
678 /// 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 }
682678
683 /// Key "parentNft".679 /// Key "parentNft".
686 }682 }
687 }683 }
688
689 /// Values.
690 pub mod value {
691 use super::*;
692
693 /// Value "ERC721Metadata".
694 pub const ERC721_METADATA: &[u8] = b"ERC721Metadata";
695
696 /// 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 }
701684
702 /// 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> {
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
71 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,
828
829 flags: RpcCollectionFlags {
827 foreign: flags.foreign,830 foreign: flags.foreign,
831 erc721metadata: flags.erc721metadata,
832 },
828 })833 })
829 }834 }
830}835}
modifiedpallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
212 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 }
218219
219 /// 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.
modifiedpallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
18}18}
1919
20/// @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 0x62e22290
22contract 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 owner
298 /// @param newOwner new owner account298 /// @param newOwner new owner account
299 /// @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;
modifiedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
55 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 )
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
35use 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;
4642
47use crate::{43use crate::{
48 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,44 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,
194}190}
195191
196#[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 contract
203 /// @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 }
212208
213 /// @notice An abbreviated name for NFTs in this contract209 /// @notice An abbreviated name for NFTs in this contract
210 /// @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 }
217215
218 /// @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")?;
230228
231 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 };
238235
239 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 e
247 ))245 ))
248 })?;246 })?;
249 if let Ok(suffix) = get_token_property(self, token_id_u32, &key::suffix()) {247
250 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 }
254
255 return Ok(base_uri + token_id.to_string().as_str());
256 }
257 }
258
259 Ok("".into())250 return Ok("".into());
251 }
252 Some(base_uri) => base_uri.into(),
253 };
254
255 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}
262263
427}428}
428429
429/// @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 }
436
437 /// @notice Function to mint token.
438 /// @param to The new owner
439 /// @return uint256 The id of the newly minted token
440 #[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 }
435449
436 /// @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 manually
439 /// @param to The new owner453 /// @param to The new owner
440 /// @param tokenId ID of the minted NFT454 /// @param tokenId ID of the minted NFT
455 #[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 }
486
487 /// @notice Function to mint token with the given tokenUri.
488 /// @param to The new owner
489 /// @param tokenUri Token URI that would be stored in the NFT properties
490 /// @return uint256 The id of the newly minted token
491 #[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 }
471506
472 /// @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 manually
475 /// @param to The new owner510 /// @param to The new owner
476 /// @param tokenId ID of the minted NFT511 /// @param tokenId ID of the minted NFT
477 /// @param tokenUri Token URI that would be stored in the NFT properties512 /// @param tokenUri Token URI that would be stored in the NFT properties
478 #[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}
552
553fn 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_METADATA
559 } else {
560 false
561 }
562}
563587
564fn 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}
579
580fn 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 }
586
587 false
588}
589603
590/// @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 contract
608 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 }
613
614 /// @notice An abbreviated name for NFTs in this contract
615 fn symbol(&self) -> Result<string> {
616 Ok(string::from_utf8_lossy(&self.token_prefix).into())
617 }
618
593 /// @notice Transfer ownership of an NFT619 /// @notice Transfer ownership of an NFT
594 /// @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` method
643 /// @param to The new owner669 /// @param to The new owner
644 /// @param tokenIds IDs of the minted NFTs670 /// @param tokenIds IDs of the minted NFTs
671 // #[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` method
679 /// @param to The new owner706 /// @param to The new owner
680 /// @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 tokens
681 #[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 )
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
407 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 }
422415
modifiedpallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
91}91}
9292
93/// @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 0x62e22290
95contract 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 owner
371 /// @param newOwner new owner account371 /// @param newOwner new owner account
372 /// @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}
386
387/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
388/// @dev See https://eips.ethereum.org/EIPS/eip-721
389/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
390contract ERC721Metadata is Dummy, ERC165 {
391 // /// @notice A descriptive name for a collection of NFTs in this contract
392 // /// @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 // }
400
401 // /// @notice An abbreviated name for NFTs in this contract
402 // /// @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 // }
410
411 /// @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 suffix
417 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
418 ///
419 /// @return token's const_metadata
420 /// @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}
386429
387/// @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 0x42966c68
401}444}
402445
403/// @dev inlined interface446/// @dev inlined interface
404contract ERC721MintableEvents {447contract ERC721UniqueMintableEvents {
405 event MintingFinished();448 event MintingFinished();
406}449}
407450
408/// @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 0x476ff149
410contract 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 }
418461
419 /// @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 manually
422 /// @param to The new owner463 /// @param to The new owner
423 /// @param tokenId ID of the minted NFT464 /// @return uint256 The id of the newly minted token
424 /// @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 }
473
474 // /// @notice Function to mint token.
475 // /// @dev `tokenId` should be obtained with `nextTokenId` method,
476 // /// unlike standard, you can't specify it manually
477 // /// @param to The new owner
478 // /// @param tokenId ID of the minted NFT
479 // /// @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 // }
433488
434 /// @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 manually
437 /// @param to The new owner490 /// @param to The new owner
438 /// @param tokenId ID of the minted NFT
439 /// @param tokenUri Token URI that would be stored in the NFT properties491 /// @param tokenUri Token URI that would be stored in the NFT properties
492 /// @return uint256 The id of the newly minted token
440 /// @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 tokenUri
446 ) 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 }
502
503 // /// @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 manually
506 // /// @param to The new owner
507 // /// @param tokenId ID of the minted NFT
508 // /// @param tokenUri Token URI that would be stored in the NFT properties
509 // /// @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 // }
454519
455 /// @dev Not implemented520 /// @dev Not implemented
456 /// @dev EVM selector for this function is: 0x7d64bcb4,521 /// @dev EVM selector for this function is: 0x7d64bcb4,
463}528}
464529
465/// @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 0x4468500d
467contract ERC721UniqueExtensions is Dummy, ERC165 {532contract ERC721UniqueExtensions is Dummy, ERC165 {
533 /// @notice A descriptive name for a collection of NFTs in this contract
534 /// @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 }
541
542 /// @notice An abbreviated name for NFTs in this contract
543 /// @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 }
550
468 /// @notice Transfer ownership of an NFT551 /// @notice Transfer ownership of an NFT
469 /// @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 tokens
526 /// @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}
536619
537/// @dev anonymous struct620/// @dev anonymous struct
538struct 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}
582
583/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
584/// @dev See https://eips.ethereum.org/EIPS/eip-721
585/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
586contract ERC721Metadata is Dummy, ERC165 {
587 /// @notice A descriptive name for a collection of NFTs in this contract
588 /// @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 }
595
596 /// @notice An abbreviated name for NFTs in this contract
597 /// @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 }
604
605 /// @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 suffix
611 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
612 ///
613 /// @return token's const_metadata
614 /// @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}
623665
624/// @dev inlined interface666/// @dev inlined interface
625contract 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 TokenProperties
776{}818{}
modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
1452 sender.clone(),
1453 sender,
1454 data,
1455 up_data_structs::CollectionFlags {
1456 external: true,
1457 ..Default::default()
1458 },
1459 );
14521460
1453 if let Err(DispatchError::Arithmetic(_)) = &collection_id {1461 if let Err(DispatchError::Arithmetic(_)) = &collection_id {
modifiedpallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth
254 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 );
259262
260 if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {263 if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
2121
22extern crate alloc;22extern crate alloc;
2323
24use 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}
192188
193#[derive(ToLog)]189#[derive(ToLog)]
194pub enum ERC721MintableEvents {190pub enum ERC721UniqueMintableEvents {
195 /// @dev Not supported191 /// @dev Not supported
196 #[allow(dead_code)]192 #[allow(dead_code)]
197 MintingFinished {},193 MintingFinished {},
198}194}
199195
200#[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 contract
199 /// @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 }
208204
209 /// @notice An abbreviated name for RFTs in this contract205 /// @notice An abbreviated name for NFTs in this contract
206 /// @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 }
213211
214 /// @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")?;
226224
227 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 };
234231
235 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 e
243 ))241 ))
244 })?;242 })?;
245 if let Ok(suffix) = get_token_property(self, token_id_u32, &key::suffix()) {243
246 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 }
250
251 return Ok(base_uri + token_id.to_string().as_str());
252 }
253 }
254
255 Ok("".into())246 return Ok("".into());
247 }
248 Some(base_uri) => base_uri.into(),
249 };
250
251 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}
258259
448}449}
449450
450/// @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 }
457
458 /// @notice Function to mint token.
459 /// @param to The new owner
460 /// @return uint256 The id of the newly minted token
461 #[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 }
456470
457 /// @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 manually
460 /// @param to The new owner474 /// @param to The new owner
461 /// @param tokenId ID of the minted RFT475 /// @param tokenId ID of the minted RFT
476 #[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 }
512
513 /// @notice Function to mint token with the given tokenUri.
514 /// @param to The new owner
515 /// @param tokenUri Token URI that would be stored in the NFT properties
516 /// @return uint256 The id of the newly minted token
517 #[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 }
497532
498 /// @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 manually
501 /// @param to The new owner536 /// @param to The new owner
502 /// @param tokenId ID of the minted RFT537 /// @param tokenId ID of the minted RFT
503 /// @param tokenUri Token URI that would be stored in the RFT properties538 /// @param tokenUri Token URI that would be stored in the RFT properties
504 #[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}
580
581fn 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_METADATA
587 } else {
588 false
589 }
590}
591615
592fn 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 contract
636 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 }
641
642 /// @notice An abbreviated name for NFTs in this contract
643 fn symbol(&self) -> Result<string> {
644 Ok(string::from_utf8_lossy(&self.token_prefix).into())
645 }
646
611 /// @notice Transfer ownership of an RFT647 /// @notice Transfer ownership of an RFT
612 /// @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` method
670 /// @param to The new owner706 /// @param to The new owner
671 /// @param tokenIds IDs of the minted RFTs707 /// @param tokenIds IDs of the minted RFTs
708 // #[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` method
712 /// @param to The new owner749 /// @param to The new owner
713 /// @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 tokens
714 #[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 )
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
9292
93use 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;
118118
119pub 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 }
377378
378 /// Destroy RFT collection379 /// Destroy RFT collection
modifiedpallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth
91}91}
9292
93/// @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 0x62e22290
95contract 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 owner
371 /// @param newOwner new owner account371 /// @param newOwner new owner account
372 /// @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}
386
387/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
388contract ERC721Metadata is Dummy, ERC165 {
389 // /// @notice A descriptive name for a collection of NFTs in this contract
390 // /// @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 // }
398
399 // /// @notice An abbreviated name for NFTs in this contract
400 // /// @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 // }
408
409 /// @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 suffix
415 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
416 ///
417 /// @return token's const_metadata
418 /// @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}
386427
387/// @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 0x42966c68
401}442}
402443
403/// @dev inlined interface444/// @dev inlined interface
404contract ERC721MintableEvents {445contract ERC721UniqueMintableEvents {
405 event MintingFinished();446 event MintingFinished();
406}447}
407448
408/// @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 0x476ff149
410contract 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 }
418459
419 /// @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 manually
422 /// @param to The new owner461 /// @param to The new owner
423 /// @param tokenId ID of the minted RFT462 /// @return uint256 The id of the newly minted token
424 /// @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 }
471
472 // /// @notice Function to mint token.
473 // /// @dev `tokenId` should be obtained with `nextTokenId` method,
474 // /// unlike standard, you can't specify it manually
475 // /// @param to The new owner
476 // /// @param tokenId ID of the minted RFT
477 // /// @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 // }
433486
434 /// @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 manually
437 /// @param to The new owner488 /// @param to The new owner
438 /// @param tokenId ID of the minted RFT
439 /// @param tokenUri Token URI that would be stored in the RFT properties489 /// @param tokenUri Token URI that would be stored in the NFT properties
490 /// @return uint256 The id of the newly minted token
440 /// @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 tokenUri
446 ) 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 }
500
501 // /// @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 manually
504 // /// @param to The new owner
505 // /// @param tokenId ID of the minted RFT
506 // /// @param tokenUri Token URI that would be stored in the RFT properties
507 // /// @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 // }
454517
455 /// @dev Not implemented518 /// @dev Not implemented
456 /// @dev EVM selector for this function is: 0x7d64bcb4,519 /// @dev EVM selector for this function is: 0x7d64bcb4,
463}526}
464527
465/// @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 0xef1eaacb
467contract ERC721UniqueExtensions is Dummy, ERC165 {530contract ERC721UniqueExtensions is Dummy, ERC165 {
531 /// @notice A descriptive name for a collection of NFTs in this contract
532 /// @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 }
539
540 /// @notice An abbreviated name for NFTs in this contract
541 /// @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 }
548
468 /// @notice Transfer ownership of an RFT549 /// @notice Transfer ownership of an RFT
469 /// @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 tokens
528 /// @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}
550631
551/// @dev anonymous struct632/// @dev anonymous struct
552struct 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}
596
597/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
598contract ERC721Metadata is Dummy, ERC165 {
599 /// @notice A descriptive name for a collection of RFTs in this contract
600 /// @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 }
607
608 /// @notice An abbreviated name for RFTs in this contract
609 /// @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 }
616
617 /// @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 suffix
623 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
624 ///
625 /// @return token's const_metadata
626 /// @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}
635677
636/// @dev inlined interface678/// @dev inlined interface
637contract 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 TokenProperties
786{}828{}
modifiedpallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
25 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};
3739
38use 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 = name
81 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_uri
85 .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}
90
91fn 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();
102
103 token_property_permissions
104 .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)))?;
113
114 if add_properties {
115 token_property_permissions
116 .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)))?;
125
126 properties
127 .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)))?;
132
133 if !base_uri_value.is_empty() {
134 properties
135 .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 }
142
143 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}
15486
155fn 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());
179108
180 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 applications
213 /// @return address Address of the newly created collection146 /// @return address Address of the newly created collection
214 #[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>)?;
238176
239 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 collection
181 /// @param name Name of the collection
182 /// @param description Informative description of the collection
183 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
184 /// @return address Address of the newly created collection
243 #[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>)?;
269
270 let address = pallet_common::eth::collection_id_to_address(collection_id);
271 Ok(address)
272 }197 }
273198
274 #[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 }
294211
295 #[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")?;
224
225 if !matches!(
226 collection.mode,
227 CollectionMode::NFT | CollectionMode::ReFungible
228 ) {
229 return Err("target collection should be either NFT or Refungible".into());
230 }
231
232 self.recorder().consume_sstore()?;
233 collection
234 .check_is_owner_or_admin(&caller)
235 .map_err(dispatch_to_evm::<T>)?;
236
237 if collection.flags.erc721metadata {
238 return Err("target collection is already Erc721Metadata compatible".into());
239 }
240 collection.flags.erc721metadata = true;
241
242 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 }
275
276 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_uri
285 .into_bytes()
286 .try_into()
287 .map_err(|_| "base uri is too large")?,
288 }],
289 )
290 .map_err(dispatch_to_evm::<T>)?;
291 }
292
293 self.recorder().consume_sstore()?;
294 collection.save().map_err(dispatch_to_evm::<T>)?;
295
296 Ok(())
297 }
316298
317 /// Check if a collection exists299 /// Check if a collection exists
318 /// @param collectionAddress Address of the collection in question300 /// @param collectionAddress Address of the collection in question
modifiedpallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth
23}23}
2424
25/// @title Contract, which allows users to operate with collections25/// @title Contract, which allows users to operate with collections
26/// @dev the ERC-165 identifier for this interface is 0x5ad4f44026/// @dev the ERC-165 identifier for this interface is 0x58918631
27contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {27contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
28 /// Create an NFT collection28 /// Create an NFT collection
29 /// @param name Name of the collection29 /// @param name Name of the collection
30 /// @param description Informative description of the collection30 /// @param description Informative description of the collection
31 /// @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 applications
32 /// @return address Address of the newly created collection32 /// @return address Address of the newly created collection
33 /// @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 tokenPrefix
45 return 0x0000000000000000000000000000000000000000;45 return 0x0000000000000000000000000000000000000000;
46 }46 }
4747
48 // /// Create an NFT collection
49 // /// @param name Name of the collection
50 // /// @param description Informative description of the collection
51 // /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
52 // /// @return address Address of the newly created collection
48 /// @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 }
6463
65 /// @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 }
7978
80 /// @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 baseUri
87 ) 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 }
9687
97 /// Check if a collection exists88 /// Check if a collection exists
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
345345
346 // =========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())?;
349349
350 Ok(())350 Ok(())
351 }351 }
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
365 /// Tokens in foreign collections can be transferred, but not burnt365 /// Tokens in foreign collections can be transferred, but not burnt
366 #[bondrewd(bits = "0..1")]366 #[bondrewd(bits = "0..1")]
367 pub foreign: bool,367 pub foreign: bool,
368 /// Supports ERC721Metadata
369 #[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` api
369 #[bondrewd(bits = "7..8")]372 #[bondrewd(bits = "7..8")]
370 pub external: bool,373 pub external: bool,
371374
372 #[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}
439
440#[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}
436448
437/// 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,
473485
474 /// Is collection is foreign.486 /// Extra collection flags
475 pub foreign: bool,487 pub flags: RpcCollectionFlags,
476}488}
477489
478/// Data used for create collection.490/// Data used for create collection.
modifiedruntime/common/dispatch.rsdiffbeforeafterboth
31};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};
3636
37#[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 params
67 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>::CollectionDecimalPointLimitExceeded
70 );71 );
71 <PalletFungible<T>>::init_collection(sender, payer, data)?72 <PalletFungible<T>>::init_collection(sender, payer, data, flags)?
72 }73 }
7374
74 #[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 }
7679
77 #[cfg(not(feature = "refungible"))]80 #[cfg(not(feature = "refungible"))]
78 CollectionMode::ReFungible => return unsupported!(T),81 CollectionMode::ReFungible => return unsupported!(T),
modifiedruntime/common/ethereum/sponsoring.rsdiffbeforeafterboth
24use 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);
modifiedtests/src/eth/allowlist.test.tsdiffbeforeafterboth
78 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();
8080
81 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);
8383
84 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;
9696
97 // 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);
9999
100 // 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();
112112
113 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);
115115
116 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;
131131
132 // 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);
134134
135 // 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;
modifiedtests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth
18}18}
1919
20/// @title Contract, which allows users to operate with collections20/// @title Contract, which allows users to operate with collections
21/// @dev the ERC-165 identifier for this interface is 0x5ad4f44021/// @dev the ERC-165 identifier for this interface is 0x58918631
22interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {22interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
23 /// Create an NFT collection23 /// Create an NFT collection
24 /// @param name Name of the collection24 /// @param name Name of the collection
25 /// @param description Informative description of the collection25 /// @param description Informative description of the collection
26 /// @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 applications
27 /// @return address Address of the newly created collection27 /// @return address Address of the newly created collection
28 /// @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 tokenPrefix
34 ) external payable returns (address);34 ) external payable returns (address);
3535
36 // /// Create an NFT collection
37 // /// @param name Name of the collection
38 // /// @param description Informative description of the collection
39 // /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
40 // /// @return address Address of the newly created collection
36 /// @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 baseUri
43 ) external payable returns (address);
4444
45 /// @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 tokenPrefix
51 ) external payable returns (address);51 ) external payable returns (address);
5252
53 /// @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 baseUri
60 ) external payable returns (address);
6156
62 /// Check if a collection exists57 /// Check if a collection exists
63 /// @param collectionAddress Address of the collection in question58 /// @param collectionAddress Address of the collection in question
modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
13}13}
1414
15/// @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 0x62e22290
17interface 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 owner
196 /// @param newOwner new owner account196 /// @param newOwner new owner account
197 /// @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}
201201
202/// @dev the ERC-165 identifier for this interface is 0x63034ac5202/// @dev the ERC-165 identifier for this interface is 0x63034ac5
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
62}62}
6363
64/// @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 0x62e22290
66interface 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 owner
245 /// @param newOwner new owner account245 /// @param newOwner new owner account
246 /// @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}
250250
251/// @dev anonymous struct251/// @dev anonymous struct
254 uint256 field_1;254 uint256 field_1;
255}255}
256
257/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
258/// @dev See https://eips.ethereum.org/EIPS/eip-721
259/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
260interface ERC721Metadata is Dummy, ERC165 {
261 // /// @notice A descriptive name for a collection of NFTs in this contract
262 // /// @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);
266
267 // /// @notice An abbreviated name for NFTs in this contract
268 // /// @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);
272
273 /// @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 suffix
279 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
280 ///
281 /// @return token's const_metadata
282 /// @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}
256286
257/// @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 0x42966c68
267}297}
268298
269/// @dev inlined interface299/// @dev inlined interface
270interface ERC721MintableEvents {300interface ERC721UniqueMintableEvents {
271 event MintingFinished();301 event MintingFinished();
272}302}
273303
274/// @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 0x476ff149
276interface 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);
280310
281 /// @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 manually
284 /// @param to The new owner312 /// @param to The new owner
285 /// @param tokenId ID of the minted NFT313 /// @return uint256 The id of the newly minted token
286 /// @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);
317
318 // /// @notice Function to mint token.
319 // /// @dev `tokenId` should be obtained with `nextTokenId` method,
320 // /// unlike standard, you can't specify it manually
321 // /// @param to The new owner
322 // /// @param tokenId ID of the minted NFT
323 // /// @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);
289326
290 /// @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 manually
293 /// @param to The new owner328 /// @param to The new owner
294 /// @param tokenId ID of the minted NFT
295 /// @param tokenUri Token URI that would be stored in the NFT properties329 /// @param tokenUri Token URI that would be stored in the NFT properties
330 /// @return uint256 The id of the newly minted token
296 /// @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,334
300 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 manually
338 // /// @param to The new owner
339 // /// @param tokenId ID of the minted NFT
340 // /// @param tokenUri Token URI that would be stored in the NFT properties
341 // /// @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);
303344
304 /// @dev Not implemented345 /// @dev Not implemented
305 /// @dev EVM selector for this function is: 0x7d64bcb4,346 /// @dev EVM selector for this function is: 0x7d64bcb4,
308}349}
309350
310/// @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 0x4468500d
312interface ERC721UniqueExtensions is Dummy, ERC165 {353interface ERC721UniqueExtensions is Dummy, ERC165 {
354 /// @notice A descriptive name for a collection of NFTs in this contract
355 /// @dev EVM selector for this function is: 0x06fdde03,
356 /// or in textual repr: name()
357 function name() external view returns (string memory);
358
359 /// @notice An abbreviated name for NFTs in this contract
360 /// @dev EVM selector for this function is: 0x95d89b41,
361 /// or in textual repr: symbol()
362 function symbol() external view returns (string memory);
363
313 /// @notice Transfer ownership of an NFT364 /// @notice Transfer ownership of an NFT
314 /// @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 tokens
351 /// @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}
355406
356/// @dev anonymous struct407/// @dev anonymous struct
357struct 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}
386
387/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
388/// @dev See https://eips.ethereum.org/EIPS/eip-721
389/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
390interface ERC721Metadata is Dummy, ERC165 {
391 /// @notice A descriptive name for a collection of NFTs in this contract
392 /// @dev EVM selector for this function is: 0x06fdde03,
393 /// or in textual repr: name()
394 function name() external view returns (string memory);
395
396 /// @notice An abbreviated name for NFTs in this contract
397 /// @dev EVM selector for this function is: 0x95d89b41,
398 /// or in textual repr: symbol()
399 function symbol() external view returns (string memory);
400
401 /// @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 suffix
407 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
408 ///
409 /// @return token's const_metadata
410 /// @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}
414437
415/// @dev inlined interface438/// @dev inlined interface
416interface 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 TokenProperties
517{}540{}
deletedtests/src/eth/api/UniqueRFT.soldiffbeforeafterboth

no changes

modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
62}62}
6363
64/// @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 0x62e22290
66interface 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 owner
245 /// @param newOwner new owner account245 /// @param newOwner new owner account
246 /// @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}
250250
251/// @dev anonymous struct251/// @dev anonymous struct
254 uint256 field_1;254 uint256 field_1;
255}255}
256
257/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
258interface ERC721Metadata is Dummy, ERC165 {
259 // /// @notice A descriptive name for a collection of NFTs in this contract
260 // /// @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);
264
265 // /// @notice An abbreviated name for NFTs in this contract
266 // /// @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);
270
271 /// @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 suffix
277 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
278 ///
279 /// @return token's const_metadata
280 /// @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}
256284
257/// @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 0x42966c68
267}295}
268296
269/// @dev inlined interface297/// @dev inlined interface
270interface ERC721MintableEvents {298interface ERC721UniqueMintableEvents {
271 event MintingFinished();299 event MintingFinished();
272}300}
273301
274/// @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 0x476ff149
276interface 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);
280308
281 /// @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 manually
284 /// @param to The new owner310 /// @param to The new owner
285 /// @param tokenId ID of the minted RFT311 /// @return uint256 The id of the newly minted token
286 /// @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);
315
316 // /// @notice Function to mint token.
317 // /// @dev `tokenId` should be obtained with `nextTokenId` method,
318 // /// unlike standard, you can't specify it manually
319 // /// @param to The new owner
320 // /// @param tokenId ID of the minted RFT
321 // /// @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);
289324
290 /// @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 manually
293 /// @param to The new owner326 /// @param to The new owner
294 /// @param tokenId ID of the minted RFT
295 /// @param tokenUri Token URI that would be stored in the RFT properties327 /// @param tokenUri Token URI that would be stored in the NFT properties
328 /// @return uint256 The id of the newly minted token
296 /// @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,332
300 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 manually
336 // /// @param to The new owner
337 // /// @param tokenId ID of the minted RFT
338 // /// @param tokenUri Token URI that would be stored in the RFT properties
339 // /// @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);
303342
304 /// @dev Not implemented343 /// @dev Not implemented
305 /// @dev EVM selector for this function is: 0x7d64bcb4,344 /// @dev EVM selector for this function is: 0x7d64bcb4,
308}347}
309348
310/// @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 0xef1eaacb
312interface ERC721UniqueExtensions is Dummy, ERC165 {351interface ERC721UniqueExtensions is Dummy, ERC165 {
352 /// @notice A descriptive name for a collection of NFTs in this contract
353 /// @dev EVM selector for this function is: 0x06fdde03,
354 /// or in textual repr: name()
355 function name() external view returns (string memory);
356
357 /// @notice An abbreviated name for NFTs in this contract
358 /// @dev EVM selector for this function is: 0x95d89b41,
359 /// or in textual repr: symbol()
360 function symbol() external view returns (string memory);
361
313 /// @notice Transfer ownership of an RFT362 /// @notice Transfer ownership of an RFT
314 /// @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 tokens
353 /// @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);
356405
357 /// Returns EVM address for refungible token406 /// Returns EVM address for refungible token
358 ///407 ///
363}412}
364413
365/// @dev anonymous struct414/// @dev anonymous struct
366struct 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}
395
396/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
397interface ERC721Metadata is Dummy, ERC165 {
398 /// @notice A descriptive name for a collection of RFTs in this contract
399 /// @dev EVM selector for this function is: 0x06fdde03,
400 /// or in textual repr: name()
401 function name() external view returns (string memory);
402
403 /// @notice An abbreviated name for RFTs in this contract
404 /// @dev EVM selector for this function is: 0x95d89b41,
405 /// or in textual repr: symbol()
406 function symbol() external view returns (string memory);
407
408 /// @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 suffix
414 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
415 ///
416 /// @return token's const_metadata
417 /// @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}
421444
422/// @dev inlined interface445/// @dev inlined interface
423interface 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 TokenProperties
522{}545{}
modifiedtests/src/eth/base.test.tsdiffbeforeafterboth
69describe('ERC165 tests', async () => {74describe('ERC165 tests', async () => {
70 // https://eips.ethereum.org/EIPS/eip-16575 // https://eips.ethereum.org/EIPS/eip-165
7176
72 let collection: number;77 let erc721MetadataCompatibleNftCollectionId: number;
78 let simpleNftCollectionId: number;
73 let minter: string;79 let minter: string;
80
81 const BASE_URI = 'base/';
7482
75 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);
86
87 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 }
7890
79 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 });
87100
88 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 });
91104
92 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 });
95108
96 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 });
99112
100 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 });
103116
104 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 });
107120
108 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 });
111124
112 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 });
115128
116 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});
120133
modifiedtests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth
3838
39 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);
4343
44 const newAdmin = helper.eth.createAccount();44 const newAdmin = helper.eth.createAccount();
5151
52 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);
5656
57 const [newAdmin] = await helper.arrange.createAccounts([10n], donor);57 const [newAdmin] = await helper.arrange.createAccounts([10n], donor);
6464
65 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');
6868
69 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);
7575
76 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');
7979
80 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);
9393
94 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');
9797
98 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);
108108
109 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');
112112
113 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);
126126
127 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');
130130
131 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);
150150
151 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');
154154
155 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);
170170
171 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');
174174
175 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);
188188
189 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');
192192
193 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);193 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
194194
210210
211 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');
214214
215 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);215 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
216216
230230
231 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');
234234
235 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);
250250
251 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');
254254
255 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);
284284
285 await collectionEvm.methods.setOwner(newOwner).send();285 await collectionEvm.methods.changeCollectionOwner(newOwner).send();
286286
287 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 });
300300
301 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);
306306
307 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);
326326
327 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);
341341
342 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);
353353
354 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;
modifiedtests/src/eth/collectionHelpersAbi.jsondiffbeforeafterboth
25 "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" }
modifiedtests/src/eth/collectionProperties.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// 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/>.
1616
17import {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';
1922
20describe('EVM collection properties', () => {23describe('EVM collection properties', () => {
21 let donor: IKeyringPair;24 let donor: IKeyringPair;
3033
31 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});
3538
36 const address = helper.ethAddress.fromCollectionId(collection.collectionId);39 const address = helper.ethAddress.fromCollectionId(collection.collectionId);
71 });74 });
72});75});
76
77describe('Supports ERC721Metadata', () => {
78 let donor: IKeyringPair;
79
80 before(async function() {
81 await usingEthPlaygrounds(async (_helper, privateKey) => {
82 donor = await privateKey({filename: __filename});
83 });
84 });
85
86 const checkERC721Metadata = async (helper: EthUniqueHelper, mode: 'nft' | 'rft') => {
87 const caller = await helper.eth.createAccountWithBalance(donor);
88 const bruh = await helper.eth.createAccountWithBalance(donor);
89
90 const BASE_URI = 'base/'
91 const SUFFIX = 'suffix1'
92 const URI = 'uri1'
93
94 const collectionHelpers = helper.ethNativeContract.collectionHelpers(caller);
95 const creatorMethod = mode === 'rft' ? 'createRFTCollection' : 'createNFTCollection'
96
97 const {collectionId, collectionAddress} = await helper.eth[creatorMethod](caller, 'n', 'd', 'p')
98
99 const contract = helper.ethNativeContract.collectionById(collectionId, mode, caller);
100 await contract.methods.addCollectionAdmin(bruh).send(); // to check that admin will work too
101
102 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;
106
107 await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, BASE_URI)
108 .send({from: bruh});
109
110 expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true;
111
112 const collection2 = await helper.nft.getCollectionObject(collectionId);
113 const data2 = await collection2.getData()
114 expect(data2?.raw.flags.erc721metadata).to.be.true;
115
116 const TPPs = data2?.raw.tokenPropertyPermissions
117 expect(TPPs?.length).to.equal(2);
118
119 expect(TPPs.find((tpp: ITokenPropertyPermission) => {
120 return tpp.key === "URI" && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner
121 })).to.be.not.null
122
123 expect(TPPs.find((tpp: ITokenPropertyPermission) => {
124 return tpp.key === "URISuffix" && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner
125 })).to.be.not.null
126
127 expect(data2?.raw.properties?.find((property: IProperty) => {
128 return property.key === "baseURI" && property.value === BASE_URI
129 })).to.be.not.null
130
131 const token1Result = await contract.methods.mint(bruh).send();
132 const tokenId1 = token1Result.events.Transfer.returnValues.tokenId;
133
134 expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI);
135
136 await contract.methods.setProperty(tokenId1, "URISuffix", Buffer.from(SUFFIX)).send();
137 expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX);
138
139 await contract.methods.setProperty(tokenId1, "URI", Buffer.from(URI)).send();
140 expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(URI);
141
142 await contract.methods.deleteProperty(tokenId1, "URI").send();
143 expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX);
144
145 const token2Result = await contract.methods.mintWithTokenURI(bruh, URI).send();
146 const tokenId2 = token2Result.events.Transfer.returnValues.tokenId;
147
148 expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(URI);
149
150 await contract.methods.deleteProperty(tokenId2, "URI").send();
151 expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI);
152
153 await contract.methods.setProperty(tokenId2, "URISuffix", Buffer.from(SUFFIX)).send();
154 expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI + SUFFIX);
155 }
156
157 itEth('ERC721Metadata property can be set for NFT collection', async({helper}) => {
158 await checkERC721Metadata(helper, 'nft');
159 });
160
161 itEth.ifWithPallets('ERC721Metadata property can be set for RFT collection', [Pallets.ReFungible], async({helper}) => {
162 await checkERC721Metadata(helper, 'rft');
163 });
164});
73165
modifiedtests/src/eth/collectionSponsoring.test.tsdiffbeforeafterboth
4444
45 await collection.addToAllowList(alice, {Ethereum: minter});45 await collection.addToAllowList(alice, {Ethereum: minter});
4646
47 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();
48
50 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);
8887
89 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);
108
109 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);109 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', '');
110110
111 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);
117114
118 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));
145142
146 {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);
155146
156 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);
226
235 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);227 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');
236
237 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);
243231
244 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');
248236
249 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));
259247
260 const userCollectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', user);248 const userCollectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', user);
249
261 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();
268252
269 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 268
285 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);
modifiedtests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth
35 const description = 'Some description';35 const description = 'Some description';
36 const prefix = 'token prefix';36 const prefix = 'token prefix';
3737
38 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');
46
47 const options = await collection.getOptions();
48
49 expect(options.tokenPropertyPermissions).to.be.empty;
45 });50 });
51
52 itEth('Create collection with properties', async ({helper}) => {
53 const owner = await helper.eth.createAccountWithBalance(donor);
54
55 const name = 'CollectionEVM';
56 const description = 'Some description';
57 const prefix = 'token prefix';
58 const baseUri = 'BaseURI';
59
60 const {collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri);
61
62 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');
69
70 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 });
4682
47 // 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;
5894
59 await collectionHelpers.methods95 await collectionHelpers.methods
60 .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.methods
69 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');
73109
74 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();
88124
89 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';
160196
161 await expect(collectionHelper.methods197 await expect(collectionHelper.methods
162 .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.methods
172 .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.methods
181 .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.methods
190 .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 });
193229
194 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 {
217253
218 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.methods
223 .setCollectionLimit('badLimit', 'true')259 .setCollectionLimit('badLimit', 'true')
modifiedtests/src/eth/createRFTCollection.test.tsdiffbeforeafterboth
37 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);
4243
43 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');
48
49 const options = await collection.getOptions();
50
51 expect(options.tokenPropertyPermissions).to.be.empty;
47 });52 });
48 53
54
55
56 itEth('Create collection with properties', async ({helper}) => {
57 const owner = await helper.eth.createAccountWithBalance(donor);
58
59 const name = 'CollectionEVM';
60 const description = 'Some description';
61 const prefix = 'token prefix';
62 const baseUri = 'BaseURI';
63
64 const {collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri);
65
66 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');
73
74 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');
75113
76 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();
90128
91 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 {
219257
220 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.methods
225 .setCollectionLimit('badLimit', 'true')263 .setCollectionLimit('badLimit', 'true')
modifiedtests/src/eth/evmCoder.test.tsdiffbeforeafterboth
65 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 {
modifiedtests/src/eth/fractionalizer/Fractionalizer.soldiffbeforeafterboth
124 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);
modifiedtests/src/eth/fractionalizer/fractionalizer.test.tsdiffbeforeafterboth
62const 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: string
64}> => {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;
6969
70 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);
9797
98 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');
125125
126 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);
148148
149 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;
153153
154 const {contract: fractionalizer} = await initContract(helper, owner);154 const {contract: fractionalizer} = await initContract(helper, owner);
155155
231231
232 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);
236236
237 const fractionalizer = await deployContract(helper, owner);237 const fractionalizer = await deployContract(helper, owner);
244244
245 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);
249249
250 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');
261261
262 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);
280280
281 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;
285285
286 const fractionalizer = await deployContract(helper, owner);286 const fractionalizer = await deployContract(helper, owner);
287287
293 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);
295295
296 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});
301301
302302
310 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);
312312
313 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;
317317
318 const {contract: fractionalizer} = await initContract(helper, owner);318 const {contract: fractionalizer} = await initContract(helper, owner);
319319
325 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);
327327
328 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;
332332
333 const {contract: fractionalizer} = await initContract(helper, owner);333 const {contract: fractionalizer} = await initContract(helper, owner);
334334
341 const owner = await helper.eth.createAccountWithBalance(donor, 20n);341 const owner = await helper.eth.createAccountWithBalance(donor, 20n);
342342
343 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 348
349 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);
355355
356 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 361
362 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 });
365365
366 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);
370370
371 const fractionalizer = await deployContract(helper, owner);371 const fractionalizer = await deployContract(helper, owner);
372372
373 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});
375375
376 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 378
379 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);
434434
435 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;
439439
440 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});
modifiedtests/src/eth/fungibleAbi.jsondiffbeforeafterboth
115 "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" }
modifiedtests/src/eth/nesting/nest.test.tsdiffbeforeafterboth
7 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');
1111
12 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 32
33 // Create a token to be nested33 // Create a token to be nested to
34 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 37
42 // Create a nested token38 // Create a nested token
43 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 42
51 // Create a token to be nested and nest43 // Create a token to be nested and nest
52 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,46
55 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 61
74 // Create a token to nest into62 // Create a token to nest into
75 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 66
82 // Create a token for nesting in the same collection as the target67 // Create a token for nesting in the same collection as the target
83 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 collection
90 const nftTokenIdB = await contractB.methods.nextTokenId().call();69 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;
70
71 // Create a token for nesting in a different collection
91 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,74
94 ).send({from: owner});
95
96 // Nest75 // Nest
110 await contract.methods.setCollectionNesting(false).send({from: owner});89 await contract.methods.setCollectionNesting(false).send({from: owner});
111 90
112 // Create a token to nest into91 // Create a token to nest into
113 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 95
121 // Create a token to nest96 // Create a token to nest
122 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,99
125 nftTokenId,
126 ).send({from: owner});
127
128 // Try to nest100 // Try to nest
129 await expect(contract.methods101 await expect(contract.methods
138 const {collectionId, contract} = await createNestingCollection(helper, owner);110 const {collectionId, contract} = await createNestingCollection(helper, owner);
139 111
140 // Mint a token112 // Mint a token
141 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 116
148 // Mint a token belonging to a different account117 // Mint a token belonging to a different account
149 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,120
152 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 account
156 await expect(contract.methods122 await expect(contract.methods
168 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});134 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});
169 135
170 // Create a token in one collection136 // Create a token in one collection
171 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 140
178 // Create a token in another collection belonging to someone else141 // Create a token in another collection
179 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,144
182 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 nest
186 await expect(contractB.methods146 await expect(contractB.methods
197 await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner});157 await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner});
198 158
199 // Create a token in one collection159 // Create a token in one collection
200 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 163
207 // Create a token in another collection164 // Create a token in another collection
208 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,167
211 nftTokenIdB,168
212 ).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 first
215 await expect(contractB.methods170 await expect(contractB.methods
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
69 expect(owner).to.equal(caller);69 expect(owner).to.equal(caller);
70 });70 });
71
72 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();
75
76 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
77
78 expect(await contract.methods.name().call()).to.equal('test');
79 expect(await contract.methods.symbol().call()).to.equal('TEST');
80 });
71});81});
7282
73describe('Check ERC721 token URI for NFT', () => {83describe('Check ERC721 token URI for NFT', () => {
79 });89 });
80 });90 });
8191
82 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();
8595
86 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 98
91 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();
97102
98 if (propertyKey && propertyValue) {103 if (propertyKey && propertyValue) {
99 // Set URL or suffix104 // Set URL or suffix
100 await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();105 await contract.methods.setProperty(tokenId, propertyKey, Buffer.from(propertyValue)).send();
101 }106 }
102107
103 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);
108113
109 return {contract, nextTokenId};114 return {contract, nextTokenId: tokenId};
110 }115 }
111116
112 itEth('Empty tokenURI', async ({helper}) => {117 itEth('Empty tokenURI', async ({helper}) => {
115 });120 });
116121
117 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 });
121126
122 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 });
126131
127 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();
148153
149 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);
156
151 const nextTokenId = await contract.methods.nextTokenId().call();157 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();
152
153 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();
159160
160 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);
165165
166 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');
167167
168 // TODO: this wont work right now, need release 919000 first168 // TODO: this wont work right now, need release 919000 first
169 // 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();
509511
510 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 );
513531
514 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);532 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
518536
519 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 );
522556
523 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);557 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
145 "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" },
modifiedtests/src/eth/payable.test.tsdiffbeforeafterboth
146 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);
148148
149 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});
164164
165 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 179
180 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 });
183183
184 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: UNLICENSED
205 pragma solidity ^0.8.6;205 pragma solidity ^0.8.6;
206206
207 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";
209209
210 error Value(uint256 value);210 error Value(uint256 value);
211211
212 contract ProxyContract {212 contract ProxyContract {
213 bool value = false;213 bool value = false;
214 address innerContract;214 address innerContract;
215215
216 event CollectionCreated(address collection);216 event CollectionCreated(address collection);
217 event TokenMinted(uint256 tokenId);217 event TokenMinted(uint256 tokenId);
218218
219 receive() external payable {}219 receive() external payable {}
220220
221 constructor() {221 constructor() {
222 innerContract = address(new InnerContract());222 innerContract = address(new InnerContract());
223 }223 }
224224
225 function flip() public {225 function flip() public {
226 value = !value;226 value = !value;
227 InnerContract(innerContract).flip();227 InnerContract(innerContract).flip();
228 }228 }
229229
230 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 }
235235
236 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 }241
242242 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 }246
247247 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',
modifiedtests/src/eth/proxy/UniqueNFTProxy.soldiffbeforeafterboth
120 return proxied.mintingFinished();120 return proxied.mintingFinished();
121 }121 }
122122
123 function mint(address to, uint256 tokenId)123 function mint(address to)
124 external124 external
125 override125 override
126 returns (bool)126 returns (uint256)
127 {127 {
128 return proxied.mint(to, tokenId);128 return proxied.mint(to);
129 }129 }
130130
131 function mintWithTokenURI(131 function mintWithTokenURI(
132 address to,132 address to,
133 uint256 tokenId,
134 string memory tokenUri133 string memory tokenUri
135 ) external override returns (bool) {134 ) external override returns (uint256) {
136 return proxied.mintWithTokenURI(to, tokenId, tokenUri);135 return proxied.mintWithTokenURI(to, tokenUri);
137 }136 }
138137
139 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 }
171170
172 function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)171 function mintBulkWithTokenURI(address to, Tuple6[] memory tokens)
173 external172 external
174 override173 override
175 returns (bool)174 returns (bool)
modifiedtests/src/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth
101101
102 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();
107107
111 await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();111 await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();
112112
113 {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',118
120 ).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 ]);
135133
136 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 });
139137
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
3131
32 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();36
37 await contract.methods.mint(caller, nextTokenId).send();37 await contract.methods.mint(caller).send();
38
38 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 });
4142
42 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);
4647
47 {
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 }
5951
60 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 });
6355
64 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);
6860
69 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;
7163
72 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);
8173
82 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);
8577
86 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);
10092
101 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);
10496
105 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 121
130 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();
137123
138 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');
143130
144 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 });
146133
147 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);
152139
153 {140 {
179166
180 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);
184171
185 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);
202189
203 const tokenId = await contract.methods.nextTokenId().call();190 const result = await contract.methods.mint(caller).send();
191 const tokenId = result.events.Transfer.returnValues.tokenId;
192
204 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);193 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
205 await contract.methods.mint(caller, tokenId).send();
206194
207 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);
246234
247 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;
249237
250 {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);
276264
277 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;
279267
280 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);268 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
281269
300 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);
305293
306 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;
308296
309 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);297 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
310298
340 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);
345333
346 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;
348336
349 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);
359347
360 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;
362350
363 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())));
381369
382 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 );
389
386 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);390 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);
390394
391 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 );
414
394 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 });
modifiedtests/src/eth/reFungibleAbi.jsondiffbeforeafterboth
145 "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" },
modifiedtests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth
76 });76 });
77 });77 });
7878
79 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();
8282
83 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 85
88 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();
94
95 if (propertyKey && propertyValue) {
96 // Set URL or suffix
97 await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();
98 }
9987
100 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);
94
95 if (propertyKey && propertyValue) {
96 // Set URL or suffix
104 expect(event.returnValues.tokenId).to.be.equal(nextTokenId);97 await contract.methods.setProperty(tokenId, propertyKey, Buffer.from(propertyValue)).send();
98 }
10599
106 return {contract, nextTokenId};100 return {contract, nextTokenId: tokenId};
107 }101 }
108102
109 itEth('Empty tokenURI', async ({helper}) => {103 itEth('Empty tokenURI', async ({helper}) => {
112 });106 });
113107
114 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 });
118112
119 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 });
123117
124 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);
299293
300 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);
304298
484 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);
486480
487 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 483
490 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;
486
492 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);
modifiedtests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth
174 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 }
176176
177 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 180
181 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)});
182182
183 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);
185185
186 return {collectionId, collectionAddress};186 return {collectionId, collectionAddress};
187 }187 }
188
189 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);
191
192 const {collectionId, collectionAddress} = await this.createNFTCollection(signer, name, description, tokenPrefix)
193
194 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
195
196 return {collectionId, collectionAddress};
197 }
188198
189 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 202
198 return {collectionId, collectionAddress};208 return {collectionId, collectionAddress};
199 }209 }
210
211 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);
213
214 const {collectionId, collectionAddress} = await this.createRFTCollection(signer, name, description, tokenPrefix)
215
216 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
217
218 return {collectionId, collectionAddress};
219 }
200220
201 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', `
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
1026 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 }
1028
1029 async getCollectionOptions(collectionId: number) {
1030 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();
1031 }
10281032
1029 /**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 }
2845
2846 async getOptions() {
2847 return await this.helper.collection.getCollectionOptions(this.collectionId);
2848 }
28412849
2842 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);