difftreelog
Merge pull request #986 from UniqueNetwork/feature/add_mint_bulk_cross
in: master
Add mintBulkCross to NFT and RFT collections
20 files changed
node/cli/src/command.rsdiffbeforeafterboth425 .map(|cfg| &cfg.registry);425 .map(|cfg| &cfg.registry);426 let task_manager =426 let task_manager =427 sc_service::TaskManager::new(runner.config().tokio_handle.clone(), *registry)427 sc_service::TaskManager::new(runner.config().tokio_handle.clone(), *registry)428 .map_err(|e| format!("Error: {:?}", e))?;428 .map_err(|e| format!("Error: {e:?}"))?;429 let info_provider = Some(timestamp_with_aura_info(12000));429 let info_provider = Some(timestamp_with_aura_info(12000));430430431 runner.async_run(|config| -> Result<(Pin<Box<dyn Future<Output = _>>>, _)> {431 runner.async_run(|config| -> Result<(Pin<Box<dyn Future<Output = _>>>, _)> {pallets/common/src/eth.rsdiffbeforeafterboth119 } else if self.sub == Default::default() {119 } else if self.sub == Default::default() {120 Ok(Some(T::CrossAccountId::from_eth(self.eth)))120 Ok(Some(T::CrossAccountId::from_eth(self.eth)))121 } else {121 } else {122 Err(format!("All fields of cross account is non zeroed {:?}", self).into())122 Err(format!("All fields of cross account is non zeroed {self:?}").into())123 }123 }124 }124 }125125pallets/nonfungible/src/erc.rsdiffbeforeafterboth26 char::{REPLACEMENT_CHARACTER, decode_utf16},26 char::{REPLACEMENT_CHARACTER, decode_utf16},27 convert::TryInto,27 convert::TryInto,28};28};29use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*};29use evm_coder::{abi::AbiType, AbiCoder, ToLog, generate_stubgen, solidity_interface, types::*};30use frame_support::BoundedVec;30use frame_support::BoundedVec;31use up_data_structs::{31use up_data_structs::{32 TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,32 TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,64 },64 },65}65}6667/// Token minting parameters68#[derive(AbiCoder, Default, Debug)]69pub struct MintTokenData {70 /// Minted token owner71 pub owner: eth::CrossAddress,72 /// Minted token properties73 pub properties: Vec<eth::Property>,74}667567frontier_contract! {76frontier_contract! {68 macro_rules! NonfungibleHandle_result {...}77 macro_rules! NonfungibleHandle_result {...}981 Ok(true)990 Ok(true)982 }991 }992993 /// @notice Function to mint a token.994 /// @param data Array of pairs of token owner and token's properties for minted token995 #[weight(<SelfWeightOf<T>>::create_multiple_items(data.len() as u32) + <SelfWeightOf<T>>::set_token_properties(data.len() as u32))]996 fn mint_bulk_cross(&mut self, caller: Caller, data: Vec<MintTokenData>) -> Result<bool> {997 let caller = T::CrossAccountId::from_eth(caller);998 let budget = self999 .recorder1000 .weight_calls_budget(<StructureWeight<T>>::find_parent());10011002 let mut create_nft_data = Vec::with_capacity(data.len());1003 for MintTokenData { owner, properties } in data {1004 let owner = owner.into_sub_cross_account::<T>()?;1005 create_nft_data.push(CreateItemData::<T> {1006 properties: properties1007 .into_iter()1008 .map(|property| property.try_into())1009 .collect::<Result<Vec<_>>>()?1010 .try_into()1011 .map_err(|_| "too many properties")?,1012 owner,1013 });1014 }10151016 <Pallet<T>>::create_multiple_items(self, &caller, create_nft_data, &budget)1017 .map_err(dispatch_to_evm::<T>)?;1018 Ok(true)1019 }9831020984 /// @notice Function to mint multiple tokens with the given tokenUris.1021 /// @notice Function to mint multiple tokens with the given tokenUris.985 /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive1022 /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutivepallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth800}800}801801802/// @title Unique extensions for ERC721.802/// @title Unique extensions for ERC721.803/// @dev the ERC-165 identifier for this interface is 0x307b061a803/// @dev the ERC-165 identifier for this interface is 0x9b397d16804contract ERC721UniqueExtensions is Dummy, ERC165 {804contract ERC721UniqueExtensions is Dummy, ERC165 {805 /// @notice A descriptive name for a collection of NFTs in this contract805 /// @notice A descriptive name for a collection of NFTs in this contract806 /// @dev EVM selector for this function is: 0x06fdde03,806 /// @dev EVM selector for this function is: 0x06fdde03,997 // return false;997 // return false;998 // }998 // }9991000 /// @notice Function to mint a token.1001 /// @param data Array of pairs of token owner and token's properties for minted token1002 /// @dev EVM selector for this function is: 0xab427b0c,1003 /// or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[])1004 function mintBulkCross(MintTokenData[] memory data) public returns (bool) {1005 require(false, stub_error);1006 data;1007 dummy = 0;1008 return false;1009 }99910101000 // /// @notice Function to mint multiple tokens with the given tokenUris.1011 // /// @notice Function to mint multiple tokens with the given tokenUris.1001 // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive1012 // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive1044 string uri;1055 string uri;1045}1056}10571058/// Token minting parameters1059struct MintTokenData {1060 /// Minted token owner1061 CrossAddress owner;1062 /// Minted token properties1063 Property[] properties;1064}104610651047/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension1066/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension1048/// @dev See https://eips.ethereum.org/EIPS/eip-7211067/// @dev See https://eips.ethereum.org/EIPS/eip-721pallets/refungible/src/erc.rsdiffbeforeafterboth26 char::{REPLACEMENT_CHARACTER, decode_utf16},26 char::{REPLACEMENT_CHARACTER, decode_utf16},27 convert::TryInto,27 convert::TryInto,28};28};29use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*};29use evm_coder::{abi::AbiType, AbiCoder, ToLog, generate_stubgen, solidity_interface, types::*};30use frame_support::{BoundedBTreeMap, BoundedVec};30use frame_support::{BoundedBTreeMap, BoundedVec};31use pallet_common::{31use pallet_common::{32 CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,32 CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,71 },71 },72}72}7374/// Token minting parameters75#[derive(AbiCoder, Default, Debug)]76pub struct OwnerPieces {77 /// Minted token owner78 pub owner: eth::CrossAddress,79 /// Number of token pieces80 pub pieces: u128,81}8283/// Token minting parameters84#[derive(AbiCoder, Default, Debug)]85pub struct MintTokenData {86 /// Minted token owner and number of pieces87 pub owners: Vec<OwnerPieces>,88 /// Minted token properties89 pub properties: Vec<eth::Property>,90}739174/// @title A contract that allows to set and delete token properties and change token property permissions.92/// @title A contract that allows to set and delete token properties and change token property permissions.75#[solidity_interface(name = TokenProperties, events(ERC721TokenEvent), enum(derive(PreDispatch)), enum_attr(weight))]93#[solidity_interface(name = TokenProperties, events(ERC721TokenEvent), enum(derive(PreDispatch)), enum_attr(weight))]1021 Ok(true)1039 Ok(true)1022 }1040 }10411042 /// @notice Function to mint a token.1043 /// @param tokenProperties Properties of minted token1044 #[weight(if token_properties.len() == 1 {1045 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(token_properties.iter().next().unwrap().owners.len() as u32)1046 } else {1047 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(token_properties.len() as u32)1048 } + <SelfWeightOf<T>>::set_token_properties(token_properties.len() as u32))]1049 fn mint_bulk_cross(1050 &mut self,1051 caller: Caller,1052 token_properties: Vec<MintTokenData>,1053 ) -> Result<bool> {1054 let caller = T::CrossAccountId::from_eth(caller);1055 let budget = self1056 .recorder1057 .weight_calls_budget(<StructureWeight<T>>::find_parent());1058 let has_multiple_tokens = token_properties.len() > 1;10591060 let mut create_rft_data = Vec::with_capacity(token_properties.len());1061 for MintTokenData { owners, properties } in token_properties {1062 let has_multiple_owners = owners.len() > 1;1063 if has_multiple_tokens & has_multiple_owners {1064 return Err(1065 "creation of multiple tokens supported only if they have single owner each"1066 .into(),1067 );1068 }1069 let users: BoundedBTreeMap<_, _, _> = owners1070 .into_iter()1071 .map(|data| Ok((data.owner.into_sub_cross_account::<T>()?, data.pieces)))1072 .collect::<Result<BTreeMap<_, _>>>()?1073 .try_into()1074 .map_err(|_| "too many users")?;1075 create_rft_data.push(CreateItemData::<T> {1076 properties: properties1077 .into_iter()1078 .map(|property| property.try_into())1079 .collect::<Result<Vec<_>>>()?1080 .try_into()1081 .map_err(|_| "too many properties")?,1082 users,1083 });1084 }10851086 <Pallet<T>>::create_multiple_items(self, &caller, create_rft_data, &budget)1087 .map_err(dispatch_to_evm::<T>)?;1088 Ok(true)1089 }102310901024 /// @notice Function to mint multiple tokens with the given tokenUris.1091 /// @notice Function to mint multiple tokens with the given tokenUris.1025 /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive1092 /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutivepallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth800}800}801801802/// @title Unique extensions for ERC721.802/// @title Unique extensions for ERC721.803/// @dev the ERC-165 identifier for this interface is 0x95c0f66c803/// @dev the ERC-165 identifier for this interface is 0x4abaabdb804contract ERC721UniqueExtensions is Dummy, ERC165 {804contract ERC721UniqueExtensions is Dummy, ERC165 {805 /// @notice A descriptive name for a collection of NFTs in this contract805 /// @notice A descriptive name for a collection of NFTs in this contract806 /// @dev EVM selector for this function is: 0x06fdde03,806 /// @dev EVM selector for this function is: 0x06fdde03,986 // return false;986 // return false;987 // }987 // }988989 /// @notice Function to mint a token.990 /// @param tokenProperties Properties of minted token991 /// @dev EVM selector for this function is: 0xdf7a5db7,992 /// or in textual repr: mintBulkCross((((address,uint256),uint128)[],(string,bytes)[])[])993 function mintBulkCross(MintTokenData[] memory tokenProperties) public returns (bool) {994 require(false, stub_error);995 tokenProperties;996 dummy = 0;997 return false;998 }988999989 // /// @notice Function to mint multiple tokens with the given tokenUris.1000 // /// @notice Function to mint multiple tokens with the given tokenUris.990 // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive1001 // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive1045 string uri;1056 string uri;1046}1057}10581059/// Token minting parameters1060struct MintTokenData {1061 /// Minted token owner and number of pieces1062 OwnerPieces[] owners;1063 /// Minted token properties1064 Property[] properties;1065}10661067/// Token minting parameters1068struct OwnerPieces {1069 /// Minted token owner1070 CrossAddress owner;1071 /// Number of token pieces1072 uint128 pieces;1073}104710741048/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension1075/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension1049/// @dev See https://eips.ethereum.org/EIPS/eip-7211076/// @dev See https://eips.ethereum.org/EIPS/eip-721pallets/scheduler-v2/src/benchmarking.rsdiffbeforeafterboth136 let bound = EncodedCall::bound() as u32;136 let bound = EncodedCall::bound() as u32;137 let mut len = match maybe_lookup_len {137 let mut len = match maybe_lookup_len {138 Some(len) => {138 Some(len) => {139 len.min(<T::Preimages as PreimageRecipient<T::Hash>>::MaxSize::get() - 2)139 len.clamp(140 bound,141 <T::Preimages as PreimageRecipient<T::Hash>>::MaxSize::get() - 2,140 .max(bound) - 3142 ) - 3141 }143 }142 None => bound.saturating_sub(4),144 None => bound.saturating_sub(4),143 };145 };pallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth25}25}262627/// @title Contract, which allows users to operate with collections27/// @title Contract, which allows users to operate with collections28/// @dev the ERC-165 identifier for this interface is 0x4135fff128/// @dev the ERC-165 identifier for this interface is 0x94e5af0d29contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {29contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {30 /// Create a collection30 /// Create a collection31 /// @return address Address of the newly created collection31 /// @return address Address of the newly created collection32 /// @dev EVM selector for this function is: 0xa765ee5b,32 /// @dev EVM selector for this function is: 0x72b5bea7,33 /// or in textual repr: createCollection(((address,uint256),string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],uint8))33 /// or in textual repr: createCollection((string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],(address,uint256),uint8))34 function createCollection(CreateCollectionData memory data) public payable returns (address) {34 function createCollection(CreateCollectionData memory data) public payable returns (address) {35 require(false, stub_error);35 require(false, stub_error);36 data;36 data;170170171/// Collection properties171/// Collection properties172struct CreateCollectionData {172struct CreateCollectionData {173 /// Collection sponsor174 CrossAddress pending_sponsor;175 /// Collection name173 /// Collection name176 string name;174 string name;177 /// Collection description175 /// Collection description192 CollectionNestingAndPermission nesting_settings;190 CollectionNestingAndPermission nesting_settings;193 /// Collection limits191 /// Collection limits194 CollectionLimitValue[] limits;192 CollectionLimitValue[] limits;193 /// Collection sponsor194 CrossAddress pending_sponsor;195 /// Extra collection flags195 /// Extra collection flags196 CollectionFlags flags;196 CollectionFlags flags;197}197}198198199/// Cross account struct200type CollectionFlags is uint8;199type CollectionFlags is uint8;201200202library CollectionFlagsLib {201library CollectionFlagsLib {207 /// External collections can't be managed using `unique` api206 /// External collections can't be managed using `unique` api208 CollectionFlags constant externalField = CollectionFlags.wrap(1);207 CollectionFlags constant externalField = CollectionFlags.wrap(1);209208210 /// Reserved bits209 /// Reserved flags211 function reservedField(uint8 value) public pure returns (CollectionFlags) {210 function reservedField(uint8 value) public pure returns (CollectionFlags) {212 require(value < 1 << 5, "out of bound value");211 require(value < 1 << 5, "out of bound value");213 return CollectionFlags.wrap(value << 1);212 return CollectionFlags.wrap(value << 1);214 }213 }215}214}215216/// Cross account struct217struct CrossAddress {218 address eth;219 uint256 sub;220}216221217/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.222/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.218struct CollectionLimitValue {223struct CollectionLimitValue {252 address[] restricted;257 address[] restricted;253}258}254255/// Cross account struct256struct CrossAddress {257 address eth;258 uint256 sub;259}260259261/// Ethereum representation of Token Property Permissions.260/// Ethereum representation of Token Property Permissions.262struct TokenPropertyPermission {261struct TokenPropertyPermission {292291293/// Type of tokens in collection292/// Type of tokens in collection294enum CollectionMode {293enum CollectionMode {294 /// Nonfungible295 Nonfungible,295 /// Fungible296 /// Fungible296 Fungible,297 Fungible,297 /// Nonfungible298 Nonfungible,299 /// Refungible298 /// Refungible300 Refungible299 Refungible301}300}runtime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth242 BurnFrom { .. }242 BurnFrom { .. }243 | BurnFromCross { .. }243 | BurnFromCross { .. }244 | MintBulk { .. }244 | MintBulk { .. }245 | MintBulkCross { .. }245 | MintBulkWithTokenUri { .. } => None,246 | MintBulkWithTokenUri { .. } => None,246247247 MintCross { .. } => withdraw_create_item::<T>(248 MintCross { .. } => withdraw_create_item::<T>(runtime/common/tests/mod.rsdiffbeforeafterboth33const PARA_ID: u32 = 2037;33const PARA_ID: u32 = 2037;343435fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {35fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {36 TPublic::Pair::from_string(&format!("//{}", seed), None)36 TPublic::Pair::from_string(&format!("//{seed}"), None)37 .expect("static values are valid; qed")37 .expect("static values are valid; qed")38 .public()38 .public()39}39}tests/src/eth/abi/nonFungible.jsondiffbeforeafterboth465 "stateMutability": "nonpayable",465 "stateMutability": "nonpayable",466 "type": "function"466 "type": "function"467 },467 },468 {469 "inputs": [470 {471 "components": [472 {473 "components": [474 { "internalType": "address", "name": "eth", "type": "address" },475 { "internalType": "uint256", "name": "sub", "type": "uint256" }476 ],477 "internalType": "struct CrossAddress",478 "name": "owner",479 "type": "tuple"480 },481 {482 "components": [483 { "internalType": "string", "name": "key", "type": "string" },484 { "internalType": "bytes", "name": "value", "type": "bytes" }485 ],486 "internalType": "struct Property[]",487 "name": "properties",488 "type": "tuple[]"489 }490 ],491 "internalType": "struct MintTokenData[]",492 "name": "data",493 "type": "tuple[]"494 }495 ],496 "name": "mintBulkCross",497 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],498 "stateMutability": "nonpayable",499 "type": "function"500 },468 {501 {469 "inputs": [502 "inputs": [470 {503 {tests/src/eth/abi/reFungible.jsondiffbeforeafterboth447 "stateMutability": "nonpayable",447 "stateMutability": "nonpayable",448 "type": "function"448 "type": "function"449 },449 },450 {451 "inputs": [452 {453 "components": [454 {455 "components": [456 {457 "components": [458 {459 "internalType": "address",460 "name": "eth",461 "type": "address"462 },463 {464 "internalType": "uint256",465 "name": "sub",466 "type": "uint256"467 }468 ],469 "internalType": "struct CrossAddress",470 "name": "owner",471 "type": "tuple"472 },473 { "internalType": "uint128", "name": "pieces", "type": "uint128" }474 ],475 "internalType": "struct OwnerPieces[]",476 "name": "owners",477 "type": "tuple[]"478 },479 {480 "components": [481 { "internalType": "string", "name": "key", "type": "string" },482 { "internalType": "bytes", "name": "value", "type": "bytes" }483 ],484 "internalType": "struct Property[]",485 "name": "properties",486 "type": "tuple[]"487 }488 ],489 "internalType": "struct MintTokenData[]",490 "name": "tokenProperties",491 "type": "tuple[]"492 }493 ],494 "name": "mintBulkCross",495 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],496 "stateMutability": "nonpayable",497 "type": "function"498 },450 {499 {451 "inputs": [500 "inputs": [452 {501 {tests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth20}20}212122/// @title Contract, which allows users to operate with collections22/// @title Contract, which allows users to operate with collections23/// @dev the ERC-165 identifier for this interface is 0x4135fff123/// @dev the ERC-165 identifier for this interface is 0x94e5af0d24interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {24interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {25 /// Create a collection25 /// Create a collection26 /// @return address Address of the newly created collection26 /// @return address Address of the newly created collection27 /// @dev EVM selector for this function is: 0xa765ee5b,27 /// @dev EVM selector for this function is: 0x72b5bea7,28 /// or in textual repr: createCollection(((address,uint256),string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],uint8))28 /// or in textual repr: createCollection((string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],(address,uint256),uint8))29 function createCollection(CreateCollectionData memory data) external payable returns (address);29 function createCollection(CreateCollectionData memory data) external payable returns (address);303031 /// Create an NFT collection31 /// Create an NFT collection103103104/// Collection properties104/// Collection properties105struct CreateCollectionData {105struct CreateCollectionData {106 /// Collection sponsor107 CrossAddress pending_sponsor;108 /// Collection name106 /// Collection name109 string name;107 string name;110 /// Collection description108 /// Collection description125 CollectionNestingAndPermission nesting_settings;123 CollectionNestingAndPermission nesting_settings;126 /// Collection limits124 /// Collection limits127 CollectionLimitValue[] limits;125 CollectionLimitValue[] limits;126 /// Collection sponsor127 CrossAddress pending_sponsor;128 /// Extra collection flags128 /// Extra collection flags129 CollectionFlags flags;129 CollectionFlags flags;130}130}131131132/// Cross account struct133type CollectionFlags is uint8;132type CollectionFlags is uint8;134133135library CollectionFlagsLib {134library CollectionFlagsLib {140 /// External collections can't be managed using `unique` api139 /// External collections can't be managed using `unique` api141 CollectionFlags constant externalField = CollectionFlags.wrap(1);140 CollectionFlags constant externalField = CollectionFlags.wrap(1);142141143 /// Reserved bits142 /// Reserved flags144 function reservedField(uint8 value) public pure returns (CollectionFlags) {143 function reservedField(uint8 value) public pure returns (CollectionFlags) {145 require(value < 1 << 5, "out of bound value");144 require(value < 1 << 5, "out of bound value");146 return CollectionFlags.wrap(value << 1);145 return CollectionFlags.wrap(value << 1);147 }146 }148}147}148149/// Cross account struct150struct CrossAddress {151 address eth;152 uint256 sub;153}149154150/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.155/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.151struct CollectionLimitValue {156struct CollectionLimitValue {185 address[] restricted;190 address[] restricted;186}191}187188/// Cross account struct189struct CrossAddress {190 address eth;191 uint256 sub;192}193192194/// Ethereum representation of Token Property Permissions.193/// Ethereum representation of Token Property Permissions.195struct TokenPropertyPermission {194struct TokenPropertyPermission {225224226/// Type of tokens in collection225/// Type of tokens in collection227enum CollectionMode {226enum CollectionMode {227 /// Nonfungible228 Nonfungible,228 /// Fungible229 /// Fungible229 Fungible,230 Fungible,230 /// Nonfungible231 Nonfungible,232 /// Refungible231 /// Refungible233 Refungible232 Refungible234}233}tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth551}551}552552553/// @title Unique extensions for ERC721.553/// @title Unique extensions for ERC721.554/// @dev the ERC-165 identifier for this interface is 0x307b061a554/// @dev the ERC-165 identifier for this interface is 0x9b397d16555interface ERC721UniqueExtensions is Dummy, ERC165 {555interface ERC721UniqueExtensions is Dummy, ERC165 {556 /// @notice A descriptive name for a collection of NFTs in this contract556 /// @notice A descriptive name for a collection of NFTs in this contract557 /// @dev EVM selector for this function is: 0x06fdde03,557 /// @dev EVM selector for this function is: 0x06fdde03,674 // /// or in textual repr: mintBulk(address,uint256[])674 // /// or in textual repr: mintBulk(address,uint256[])675 // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool);675 // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool);676677 /// @notice Function to mint a token.678 /// @param data Array of pairs of token owner and token's properties for minted token679 /// @dev EVM selector for this function is: 0xab427b0c,680 /// or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[])681 function mintBulkCross(MintTokenData[] memory data) external returns (bool);676682677 // /// @notice Function to mint multiple tokens with the given tokenUris.683 // /// @notice Function to mint multiple tokens with the given tokenUris.678 // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive684 // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive705 string uri;711 string uri;706}712}713714/// Token minting parameters715struct MintTokenData {716 /// Minted token owner717 CrossAddress owner;718 /// Minted token properties719 Property[] properties;720}707721708/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension722/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension709/// @dev See https://eips.ethereum.org/EIPS/eip-721723/// @dev See https://eips.ethereum.org/EIPS/eip-721tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth551}551}552552553/// @title Unique extensions for ERC721.553/// @title Unique extensions for ERC721.554/// @dev the ERC-165 identifier for this interface is 0x95c0f66c554/// @dev the ERC-165 identifier for this interface is 0x4abaabdb555interface ERC721UniqueExtensions is Dummy, ERC165 {555interface ERC721UniqueExtensions is Dummy, ERC165 {556 /// @notice A descriptive name for a collection of NFTs in this contract556 /// @notice A descriptive name for a collection of NFTs in this contract557 /// @dev EVM selector for this function is: 0x06fdde03,557 /// @dev EVM selector for this function is: 0x06fdde03,668 // /// or in textual repr: mintBulk(address,uint256[])668 // /// or in textual repr: mintBulk(address,uint256[])669 // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool);669 // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool);670671 /// @notice Function to mint a token.672 /// @param tokenProperties Properties of minted token673 /// @dev EVM selector for this function is: 0xdf7a5db7,674 /// or in textual repr: mintBulkCross((((address,uint256),uint128)[],(string,bytes)[])[])675 function mintBulkCross(MintTokenData[] memory tokenProperties) external returns (bool);670676671 // /// @notice Function to mint multiple tokens with the given tokenUris.677 // /// @notice Function to mint multiple tokens with the given tokenUris.672 // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive678 // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive706 string uri;712 string uri;707}713}714715/// Token minting parameters716struct MintTokenData {717 /// Minted token owner and number of pieces718 OwnerPieces[] owners;719 /// Minted token properties720 Property[] properties;721}722723/// Token minting parameters724struct OwnerPieces {725 /// Minted token owner726 CrossAddress owner;727 /// Number of token pieces728 uint128 pieces;729}708730709/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension731/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension710/// @dev See https://eips.ethereum.org/EIPS/eip-721732/// @dev See https://eips.ethereum.org/EIPS/eip-721tests/src/eth/nonFungible.test.tsdiffbeforeafterboth18import {IKeyringPair} from '@polkadot/types/types';18import {IKeyringPair} from '@polkadot/types/types';19import {Contract} from 'web3-eth-contract';19import {Contract} from 'web3-eth-contract';20import {ITokenPropertyPermission} from '../util/playgrounds/types';20import {ITokenPropertyPermission} from '../util/playgrounds/types';21import {CREATE_COLLECTION_DATA_DEFAULTS, CollectionMode, CreateCollectionData, TokenPermissionField} from './util/playgrounds/types';212222describe('Check ERC721 token URI for NFT', () => {23describe('Check ERC721 token URI for NFT', () => {23 let donor: IKeyringPair;24 let donor: IKeyringPair;197 }198 }198 });199 });200201 itEth('Can perform mintBulkCross()', async ({helper}) => {202 const caller = await helper.eth.createAccountWithBalance(donor);203 const callerCross = helper.ethCrossAccount.fromAddress(caller);204 const receiver = helper.eth.createAccount();205 const receiverCross = helper.ethCrossAccount.fromAddress(receiver);206207 const permissions = [208 {code: TokenPermissionField.Mutable, value: true},209 {code: TokenPermissionField.TokenOwner, value: true},210 {code: TokenPermissionField.CollectionAdmin, value: true},211 ];212 const {collectionAddress} = await helper.eth.createCollection(213 caller,214 {215 ...CREATE_COLLECTION_DATA_DEFAULTS,216 name: 'A',217 description: 'B',218 tokenPrefix: 'C',219 collectionMode: 'nft',220 adminList: [callerCross],221 tokenPropertyPermissions: [222 {key: 'key_0_0', permissions},223 {key: 'key_1_0', permissions},224 {key: 'key_1_1', permissions},225 {key: 'key_2_0', permissions},226 {key: 'key_2_1', permissions},227 {key: 'key_2_2', permissions},228 ],229 },230 ).send();231232 const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);233 {234 const nextTokenId = await contract.methods.nextTokenId().call();235 expect(nextTokenId).to.be.equal('1');236 const result = await contract.methods.mintBulkCross([237 {238 owner: receiverCross,239 properties: [240 {key: 'key_0_0', value: Buffer.from('value_0_0')},241 ],242 },243 {244 owner: receiverCross,245 properties: [246 {key: 'key_1_0', value: Buffer.from('value_1_0')},247 {key: 'key_1_1', value: Buffer.from('value_1_1')},248 ],249 },250 {251 owner: receiverCross,252 properties: [253 {key: 'key_2_0', value: Buffer.from('value_2_0')},254 {key: 'key_2_1', value: Buffer.from('value_2_1')},255 {key: 'key_2_2', value: Buffer.from('value_2_2')},256 ],257 },258 ]).send({from: caller});259 const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);260 const bulkSize = 3;261 for(let i = 0; i < bulkSize; i++) {262 const event = events[i];263 expect(event.address).to.equal(collectionAddress);264 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');265 expect(event.returnValues.to).to.equal(receiver);266 expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);267 }268269 const properties = [270 await contract.methods.properties(+nextTokenId, []).call(),271 await contract.methods.properties(+nextTokenId + 1, []).call(),272 await contract.methods.properties(+nextTokenId + 2, []).call(),273 ];274 expect(properties).to.be.deep.equal([275 [276 ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')],277 ],278 [279 ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')],280 ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')],281 ],282 [283 ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],284 ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],285 ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],286 ],287 ]);288 }289 });199290200 itEth('Can perform burn()', async ({helper}) => {291 itEth('Can perform burn()', async ({helper}) => {201 const caller = await helper.eth.createAccountWithBalance(donor);292 const caller = await helper.eth.createAccountWithBalance(donor);tests/src/eth/reFungible.test.tsdiffbeforeafterboth18import {expect, itEth, usingEthPlaygrounds} from './util';18import {expect, itEth, usingEthPlaygrounds} from './util';19import {IKeyringPair} from '@polkadot/types/types';19import {IKeyringPair} from '@polkadot/types/types';20import {ITokenPropertyPermission} from '../util/playgrounds/types';20import {ITokenPropertyPermission} from '../util/playgrounds/types';21import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types';212222describe('Refungible: Plain calls', () => {23describe('Refungible: Plain calls', () => {23 let donor: IKeyringPair;24 let donor: IKeyringPair;125 }126 }126 });127 });127128129 itEth('Can perform mintBulkCross() with multiple tokens', async ({helper}) => {130 const caller = await helper.eth.createAccountWithBalance(donor);131 const callerCross = helper.ethCrossAccount.fromAddress(caller);132 const receiver = helper.eth.createAccount();133 const receiverCross = helper.ethCrossAccount.fromAddress(receiver);134135 const permissions = [136 {code: TokenPermissionField.Mutable, value: true},137 {code: TokenPermissionField.TokenOwner, value: true},138 {code: TokenPermissionField.CollectionAdmin, value: true},139 ];140 const {collectionAddress} = await helper.eth.createCollection(141 caller,142 {143 ...CREATE_COLLECTION_DATA_DEFAULTS,144 name: 'A',145 description: 'B',146 tokenPrefix: 'C',147 collectionMode: 'rft',148 adminList: [callerCross],149 tokenPropertyPermissions: [150 {key: 'key_0_0', permissions},151 {key: 'key_1_0', permissions},152 {key: 'key_1_1', permissions},153 {key: 'key_2_0', permissions},154 {key: 'key_2_1', permissions},155 {key: 'key_2_2', permissions},156 ],157 },158 ).send();159160 const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);161 const nextTokenId = await contract.methods.nextTokenId().call();162 expect(nextTokenId).to.be.equal('1');163 const result = await contract.methods.mintBulkCross([164 {165 owners: [{166 owner: receiverCross,167 pieces: 1,168 }],169 properties: [170 {key: 'key_0_0', value: Buffer.from('value_0_0')},171 ],172 },173 {174 owners: [{175 owner: receiverCross,176 pieces: 2,177 }],178 properties: [179 {key: 'key_1_0', value: Buffer.from('value_1_0')},180 {key: 'key_1_1', value: Buffer.from('value_1_1')},181 ],182 },183 {184 owners: [{185 owner: receiverCross,186 pieces: 1,187 }],188 properties: [189 {key: 'key_2_0', value: Buffer.from('value_2_0')},190 {key: 'key_2_1', value: Buffer.from('value_2_1')},191 {key: 'key_2_2', value: Buffer.from('value_2_2')},192 ],193 },194 ]).send({from: caller});195 const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);196 const bulkSize = 3;197 for(let i = 0; i < bulkSize; i++) {198 const event = events[i];199 expect(event.address).to.equal(collectionAddress);200 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');201 expect(event.returnValues.to).to.equal(receiver);202 expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);203 }204205 const properties = [206 await contract.methods.properties(+nextTokenId, []).call(),207 await contract.methods.properties(+nextTokenId + 1, []).call(),208 await contract.methods.properties(+nextTokenId + 2, []).call(),209 ];210 expect(properties).to.be.deep.equal([211 [212 ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')],213 ],214 [215 ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')],216 ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')],217 ],218 [219 ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],220 ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],221 ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],222 ],223 ]);224 });225226 itEth('Can perform mintBulkCross() with multiple owners', async ({helper}) => {227 const caller = await helper.eth.createAccountWithBalance(donor);228 const callerCross = helper.ethCrossAccount.fromAddress(caller);229 const receiver = helper.eth.createAccount();230 const receiverCross = helper.ethCrossAccount.fromAddress(receiver);231 const receiver2 = helper.eth.createAccount();232 const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2);233234 const permissions = [235 {code: TokenPermissionField.Mutable, value: true},236 {code: TokenPermissionField.TokenOwner, value: true},237 {code: TokenPermissionField.CollectionAdmin, value: true},238 ];239 const {collectionAddress} = await helper.eth.createCollection(240 caller,241 {242 ...CREATE_COLLECTION_DATA_DEFAULTS,243 name: 'A',244 description: 'B',245 tokenPrefix: 'C',246 collectionMode: 'rft',247 adminList: [callerCross],248 tokenPropertyPermissions: [249 {key: 'key_2_0', permissions},250 {key: 'key_2_1', permissions},251 {key: 'key_2_2', permissions},252 ],253 },254 ).send();255256 const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);257 const nextTokenId = await contract.methods.nextTokenId().call();258 expect(nextTokenId).to.be.equal('1');259 const result = await contract.methods.mintBulkCross([{260 owners: [261 {262 owner: receiverCross,263 pieces: 1,264 },265 {266 owner: receiver2Cross,267 pieces: 2,268 },269 ],270 properties: [271 {key: 'key_2_0', value: Buffer.from('value_2_0')},272 {key: 'key_2_1', value: Buffer.from('value_2_1')},273 {key: 'key_2_2', value: Buffer.from('value_2_2')},274 ],275 }]).send({from: caller});276 const event = result.events.Transfer;277 expect(event.address).to.equal(collectionAddress);278 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');279 expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');280 expect(event.returnValues.tokenId).to.equal(`${+nextTokenId}`);281282 const properties = [283 await contract.methods.properties(+nextTokenId, []).call(),284 ];285 expect(properties).to.be.deep.equal([[286 ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],287 ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],288 ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],289 ]]);290 });291128 itEth('Can perform setApprovalForAll()', async ({helper}) => {292 itEth('Can perform setApprovalForAll()', async ({helper}) => {129 const owner = await helper.eth.createAccountWithBalance(donor);293 const owner = await helper.eth.createAccountWithBalance(donor);785 await contract.methods.setApprovalForAll(spender, false).send({from: owner});949 await contract.methods.setApprovalForAll(spender, false).send({from: owner});786950787 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;951 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;952 });953954 itEth('[negative] Can perform mintBulkCross() with multiple owners and multiple tokens', async ({helper}) => {955 const caller = await helper.eth.createAccountWithBalance(donor);956 const callerCross = helper.ethCrossAccount.fromAddress(caller);957 const receiver = helper.eth.createAccount();958 const receiverCross = helper.ethCrossAccount.fromAddress(receiver);959 const receiver2 = helper.eth.createAccount();960 const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2);961962 const permissions = [963 {code: TokenPermissionField.Mutable, value: true},964 {code: TokenPermissionField.TokenOwner, value: true},965 {code: TokenPermissionField.CollectionAdmin, value: true},966 ];967 const {collectionAddress} = await helper.eth.createCollection(968 caller,969 {970 ...CREATE_COLLECTION_DATA_DEFAULTS,971 name: 'A',972 description: 'B',973 tokenPrefix: 'C',974 collectionMode: 'rft',975 adminList: [callerCross],976 tokenPropertyPermissions: [977 {key: 'key_0_0', permissions},978 {key: 'key_2_0', permissions},979 {key: 'key_2_1', permissions},980 {key: 'key_2_2', permissions},981 ],982 },983 ).send();984985 const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);986 const nextTokenId = await contract.methods.nextTokenId().call();987 expect(nextTokenId).to.be.equal('1');988 const createData = [989 {990 owners: [{991 owner: receiverCross,992 pieces: 1,993 }],994 properties: [995 {key: 'key_0_0', value: Buffer.from('value_0_0')},996 ],997 },998 {999 owners: [1000 {1001 owner: receiverCross,1002 pieces: 1,1003 },1004 {1005 owner: receiver2Cross,1006 pieces: 2,1007 },1008 ],1009 properties: [1010 {key: 'key_2_0', value: Buffer.from('value_2_0')},1011 {key: 'key_2_1', value: Buffer.from('value_2_1')},1012 {key: 'key_2_2', value: Buffer.from('value_2_2')},1013 ],1014 },1015 ];10161017 await expect(contract.methods.mintBulkCross(createData).call({from: caller})).to.be.rejectedWith('creation of multiple tokens supported only if they have single owner each');788 });1018 });789});1019});7901020