difftreelog
Merge pull request #869 from UniqueNetwork/feature/generalize_2_methods
in: master
Feature/generalize_2_methods
34 files changed
Cargo.lockdiffbeforeafterboth636163616362[[package]]6362[[package]]6363name = "pallet-fungible"6363name = "pallet-fungible"6364version = "0.1.9"6364version = "0.1.10"6365dependencies = [6365dependencies = [6366 "evm-coder",6366 "evm-coder",6367 "frame-benchmarking",6367 "frame-benchmarking",677467746775[[package]]6775[[package]]6776name = "pallet-refungible"6776name = "pallet-refungible"6777version = "0.2.12"6777version = "0.2.13"6778dependencies = [6778dependencies = [6779 "evm-coder",6779 "evm-coder",6780 "frame-benchmarking",6780 "frame-benchmarking",pallets/common/src/eth.rsdiffbeforeafterboth20use sp_std::{vec, vec::Vec};20use sp_std::{vec, vec::Vec};21use evm_coder::{AbiCoder, types::Address};21use evm_coder::{22 AbiCoder,23 types::{Address, String},24};22pub use pallet_evm::{Config, account::CrossAccountId};25pub use pallet_evm::{Config, account::CrossAccountId};23use sp_core::{H160, U256};26use sp_core::{H160, U256};390 }393 }391}394}395396/// Data for creation token with uri.397#[derive(Debug, AbiCoder)]398pub struct TokenUri {399 /// Id of new token.400 pub id: U256,401402 /// Uri of new token.403 pub uri: String,404}392405393/// Nested collections.406/// Nested collections.394#[derive(Debug, Default, AbiCoder)]407#[derive(Debug, Default, AbiCoder)]pallets/common/src/lib.rsdiffbeforeafterboth66 ensure,66 ensure,67 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},67 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},68 dispatch::Pays,68 dispatch::Pays,69 transactional,69 transactional, fail,70};70};71use pallet_evm::GasWeightMapping;71use pallet_evm::GasWeightMapping;72use up_data_structs::{72use up_data_structs::{73 AccessMode,73 COLLECTION_NUMBER_LIMIT,74 COLLECTION_NUMBER_LIMIT,74 Collection,75 Collection,75 RpcCollection,76 RpcCollection,125use sp_core::H160;126use sp_core::H160;126use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};127use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};128129use crate::erc::CollectionHelpersEvents;127#[cfg(feature = "runtime-benchmarks")]130#[cfg(feature = "runtime-benchmarks")]128pub mod benchmarking;131pub mod benchmarking;129pub mod dispatch;132pub mod dispatch;1264 Ok(())1267 Ok(())1265 }1268 }12691270 /// A batch operation to add, edit or remove properties for a token.1271 /// It sets or removes a token's properties according to1272 /// `properties_updates` contents:1273 /// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`1274 /// * removes a property under the <key> if the value is `None` `(<key>, None)`.1275 ///1276 /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.1277 /// - `is_token_create`: Indicates that method is called during token initialization.1278 /// Allows to bypass ownership check.1279 ///1280 /// All affected properties should have `mutable` permission1281 /// to be **deleted** or to be **set more than once**,1282 /// and the sender should have permission to edit those properties.1283 ///1284 /// This function fires an event for each property change.1285 /// In case of an error, all the changes (including the events) will be reverted1286 /// since the function is transactional.1287 pub fn modify_token_properties(1288 collection: &CollectionHandle<T>,1289 sender: &T::CrossAccountId,1290 token_id: TokenId,1291 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1292 is_token_create: bool,1293 mut stored_properties: Properties,1294 is_token_owner: impl Fn() -> Result<bool, DispatchError>,1295 set_token_properties: impl FnOnce(Properties),1296 ) -> DispatchResult {1297 let is_collection_admin = collection.is_owner_or_admin(sender);1298 let permissions = Self::property_permissions(collection.id);12991300 let mut token_owner_result = None;1301 let mut is_token_owner = || -> Result<bool, DispatchError> {1302 *token_owner_result.get_or_insert_with(&is_token_owner)1303 };13041305 for (key, value) in properties_updates {1306 let permission = permissions1307 .get(&key)1308 .cloned()1309 .unwrap_or_else(PropertyPermission::none);13101311 let is_property_exists = stored_properties.get(&key).is_some();13121313 match permission {1314 PropertyPermission { mutable: false, .. } if is_property_exists => {1315 return Err(<Error<T>>::NoPermission.into());1316 }13171318 PropertyPermission {1319 collection_admin,1320 token_owner,1321 ..1322 } => {1323 //TODO: investigate threats during public minting.1324 let is_token_create =1325 is_token_create && (collection_admin || token_owner) && value.is_some();1326 if !(is_token_create1327 || (collection_admin && is_collection_admin)1328 || (token_owner && is_token_owner()?))1329 {1330 fail!(<Error<T>>::NoPermission);1331 }1332 }1333 }13341335 match value {1336 Some(value) => {1337 stored_properties1338 .try_set(key.clone(), value)1339 .map_err(<Error<T>>::from)?;13401341 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1342 }1343 None => {1344 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13451346 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1347 }1348 }13491350 <PalletEvm<T>>::deposit_log(1351 CollectionHelpersEvents::TokenChanged {1352 collection_id: eth::collection_id_to_address(collection.id),1353 token_id: token_id.into(),1354 }1355 .to_log(T::ContractAddress::get()),1356 );1357 }13581359 set_token_properties(stored_properties);13601361 Ok(())1362 }13631364 /// Sets or unsets the approval of a given operator.1365 ///1366 /// The `operator` is allowed to transfer all token pieces of the `owner` on their behalf.1367 /// - `owner`: Token owner1368 /// - `operator`: Operator1369 /// - `approve`: Should operator status be granted or revoked?1370 pub fn set_allowance_for_all(1371 collection: &CollectionHandle<T>,1372 owner: &T::CrossAccountId,1373 operator: &T::CrossAccountId,1374 approve: bool,1375 set_allowance: impl FnOnce(),1376 log: evm_coder::ethereum::Log,1377 ) -> DispatchResult {1378 if collection.permissions.access() == AccessMode::AllowList {1379 collection.check_allowlist(owner)?;1380 collection.check_allowlist(operator)?;1381 }13821383 Self::ensure_correct_receiver(operator)?;13841385 set_allowance();13861387 <PalletEvm<T>>::deposit_log(log);1388 Self::deposit_event(Event::ApprovedForAll(1389 collection.id,1390 owner.clone(),1391 operator.clone(),1392 approve,1393 ));1394 Ok(())1395 }126613961267 /// Set collection property.1397 /// Set collection property.1268 ///1398 ///pallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/CHANGELOG.mddiffbeforeafterboth445<!-- bureaucrate goes here -->5<!-- bureaucrate goes here -->667## [0.1.10] - 2023-02-0189### Added1011- The functions `allowanceCross` to `ERC20UniqueExtensions` interface.127## [0.1.9] - 2022-12-0113## [0.1.9] - 2022-12-018149### Added15### Addedpallets/fungible/Cargo.tomldiffbeforeafterboth2edition = "2021"2edition = "2021"3license = "GPLv3"3license = "GPLv3"4name = "pallet-fungible"4name = "pallet-fungible"5version = "0.1.9"5version = "0.1.10"667[dependencies]7[dependencies]8# Note: `package = "parity-scale-codec"` must be supplied since the `Encode` macro searches for it.8# Note: `package = "parity-scale-codec"` must be supplied since the `Encode` macro searches for it.pallets/fungible/src/erc.rsdiffbeforeafterboth19extern crate alloc;19extern crate alloc;20use core::char::{REPLACEMENT_CHARACTER, decode_utf16};20use core::char::{REPLACEMENT_CHARACTER, decode_utf16};21use core::convert::TryInto;21use core::convert::TryInto;22use evm_coder::AbiCoder;22use evm_coder::{23use evm_coder::{23 abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*,24 abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*,24 weight,25 weight,27use pallet_common::{28use pallet_common::{28 CollectionHandle,29 CollectionHandle,29 erc::{CommonEvmHandler, PrecompileResult, CollectionCall},30 erc::{CommonEvmHandler, PrecompileResult, CollectionCall},31 eth::CrossAddress,30};32};31use sp_std::vec::Vec;33use sp_std::vec::Vec;32use pallet_evm::{account::CrossAccountId, PrecompileHandle};34use pallet_evm::{account::CrossAccountId, PrecompileHandle};57 },59 },58}60}6162#[derive(AbiCoder, Debug)]63pub struct AmountForAddress {64 to: Address,65 amount: U256,66}596760#[solidity_interface(name = ERC20, events(ERC20Events), expect_selector = 0x942e8b22)]68#[solidity_interface(name = ERC20, events(ERC20Events), expect_selector = 0x942e8b22)]61impl<T: Config> FungibleHandle<T> {69impl<T: Config> FungibleHandle<T> {161where169where162 T::AccountId: From<[u8; 32]>,170 T::AccountId: From<[u8; 32]>,163{171{172 /// @dev Function to check the amount of tokens that an owner allowed to a spender.173 /// @param owner crossAddress The address which owns the funds.174 /// @param spender crossAddress The address which will spend the funds.175 /// @return A uint256 specifying the amount of tokens still available for the spender.176 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {177 let owner = owner.into_sub_cross_account::<T>()?;178 let spender = spender.into_sub_cross_account::<T>()?;179180 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())181 }182164 /// @notice A description for the collection.183 /// @notice A description for the collection.165 fn description(&self) -> Result<String> {184 fn description(&self) -> Result<String> {172 fn mint_cross(191 fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {173 &mut self,174 caller: Caller,175 to: pallet_common::eth::CrossAddress,176 amount: U256,177 ) -> Result<bool> {178 let caller = T::CrossAccountId::from_eth(caller);192 let caller = T::CrossAccountId::from_eth(caller);179 let to = to.into_sub_cross_account::<T>()?;193 let to = to.into_sub_cross_account::<T>()?;190 fn approve_cross(204 fn approve_cross(191 &mut self,205 &mut self,192 caller: Caller,206 caller: Caller,193 spender: pallet_common::eth::CrossAddress,207 spender: CrossAddress,194 amount: U256,208 amount: U256,195 ) -> Result<bool> {209 ) -> Result<bool> {196 let caller = T::CrossAccountId::from_eth(caller);210 let caller = T::CrossAccountId::from_eth(caller);231 fn burn_from_cross(245 fn burn_from_cross(232 &mut self,246 &mut self,233 caller: Caller,247 caller: Caller,234 from: pallet_common::eth::CrossAddress,248 from: CrossAddress,235 amount: U256,249 amount: U256,236 ) -> Result<bool> {250 ) -> Result<bool> {237 let caller = T::CrossAccountId::from_eth(caller);251 let caller = T::CrossAccountId::from_eth(caller);249 /// Mint tokens for multiple accounts.263 /// Mint tokens for multiple accounts.250 /// @param amounts array of pairs of account address and amount264 /// @param amounts array of pairs of account address and amount251 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]265 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]252 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<(Address, U256)>) -> Result<bool> {266 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {253 let caller = T::CrossAccountId::from_eth(caller);267 let caller = T::CrossAccountId::from_eth(caller);254 let budget = self268 let budget = self255 .recorder269 .recorder256 .weight_calls_budget(<StructureWeight<T>>::find_parent());270 .weight_calls_budget(<StructureWeight<T>>::find_parent());257 let amounts = amounts271 let amounts = amounts258 .into_iter()272 .into_iter()259 .map(|(to, amount)| {273 .map(|AmountForAddress { to, amount }| {260 Ok((274 Ok((261 T::CrossAccountId::from_eth(to),275 T::CrossAccountId::from_eth(to),262 amount.try_into().map_err(|_| "amount overflow")?,276 amount.try_into().map_err(|_| "amount overflow")?,273 fn transfer_cross(287 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {274 &mut self,275 caller: Caller,276 to: pallet_common::eth::CrossAddress,277 amount: U256,278 ) -> Result<bool> {279 let caller = T::CrossAccountId::from_eth(caller);288 let caller = T::CrossAccountId::from_eth(caller);280 let to = to.into_sub_cross_account::<T>()?;289 let to = to.into_sub_cross_account::<T>()?;291 fn transfer_from_cross(300 fn transfer_from_cross(292 &mut self,301 &mut self,293 caller: Caller,302 caller: Caller,294 from: pallet_common::eth::CrossAddress,303 from: CrossAddress,295 to: pallet_common::eth::CrossAddress,304 to: CrossAddress,296 amount: U256,305 amount: U256,297 ) -> Result<bool> {306 ) -> Result<bool> {298 let caller = T::CrossAccountId::from_eth(caller);307 let caller = T::CrossAccountId::from_eth(caller);pallets/fungible/src/lib.rsdiffbeforeafterboth165 pub type Allowance<T: Config> = StorageNMap<165 pub type Allowance<T: Config> = StorageNMap<166 Key = (166 Key = (167 Key<Twox64Concat, CollectionId>,167 Key<Twox64Concat, CollectionId>,168 Key<Blake2_128, T::CrossAccountId>,168 Key<Blake2_128, T::CrossAccountId>, // Owner169 Key<Blake2_128Concat, T::CrossAccountId>,169 Key<Blake2_128Concat, T::CrossAccountId>, // Spender170 ),170 ),171 Value = u128,171 Value = u128,172 QueryKind = ValueQuery,172 QueryKind = ValueQuery,pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth511 bytes value;511 bytes value;512}512}513513514/// @dev the ERC-165 identifier for this interface is 0x65789571514/// @dev the ERC-165 identifier for this interface is 0x85d7dea6515contract ERC20UniqueExtensions is Dummy, ERC165 {515contract ERC20UniqueExtensions is Dummy, ERC165 {516 /// @dev Function to check the amount of tokens that an owner allowed to a spender.517 /// @param owner crossAddress The address which owns the funds.518 /// @param spender crossAddress The address which will spend the funds.519 /// @return A uint256 specifying the amount of tokens still available for the spender.520 /// @dev EVM selector for this function is: 0xe0af4bd7,521 /// or in textual repr: allowanceCross((address,uint256),(address,uint256))522 function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) public view returns (uint256) {523 require(false, stub_error);524 owner;525 spender;526 dummy;527 return 0;528 }529516 /// @notice A description for the collection.530 /// @notice A description for the collection.517 /// @dev EVM selector for this function is: 0x7284e416,531 /// @dev EVM selector for this function is: 0x7284e416,576 /// @param amounts array of pairs of account address and amount590 /// @param amounts array of pairs of account address and amount577 /// @dev EVM selector for this function is: 0x1acf2d55,591 /// @dev EVM selector for this function is: 0x1acf2d55,578 /// or in textual repr: mintBulk((address,uint256)[])592 /// or in textual repr: mintBulk((address,uint256)[])579 function mintBulk(Tuple9[] memory amounts) public returns (bool) {593 function mintBulk(AmountForAddress[] memory amounts) public returns (bool) {580 require(false, stub_error);594 require(false, stub_error);581 amounts;595 amounts;582 dummy = 0;596 dummy = 0;618 }632 }619}633}620634621/// @dev anonymous struct622struct Tuple9 {635struct AmountForAddress {623 address field_0;636 address to;624 uint256 field_1;637 uint256 amount;625}638}626639627/// @dev the ERC-165 identifier for this interface is 0x40c10f19640/// @dev the ERC-165 identifier for this interface is 0x40c10f19pallets/nonfungible/src/erc.rsdiffbeforeafterboth38use pallet_common::{38use pallet_common::{39 CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,39 CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,40 erc::{CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key},40 erc::{CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key},41 eth,41 eth::{self, TokenUri},42};42};43use pallet_evm::{account::CrossAccountId, PrecompileHandle};43use pallet_evm::{account::CrossAccountId, PrecompileHandle};44use pallet_evm_coder_substrate::call;44use pallet_evm_coder_substrate::call;948 &mut self,948 &mut self,949 caller: Caller,949 caller: Caller,950 to: Address,950 to: Address,951 tokens: Vec<(U256, String)>,951 tokens: Vec<TokenUri>,952 ) -> Result<bool> {952 ) -> Result<bool> {953 let key = key::url();953 let key = key::url();954 let caller = T::CrossAccountId::from_eth(caller);954 let caller = T::CrossAccountId::from_eth(caller);961 .weight_calls_budget(<StructureWeight<T>>::find_parent());961 .weight_calls_budget(<StructureWeight<T>>::find_parent());962962963 let mut data = Vec::with_capacity(tokens.len());963 let mut data = Vec::with_capacity(tokens.len());964 for (id, token_uri) in tokens {964 for TokenUri { id, uri } in tokens {965 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;965 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;966 if id != expected_index {966 if id != expected_index {967 return Err("item id should be next".into());967 return Err("item id should be next".into());972 properties972 properties973 .try_push(Property {973 .try_push(Property {974 key: key.clone(),974 key: key.clone(),975 value: token_uri975 value: uri976 .into_bytes()976 .into_bytes()977 .try_into()977 .try_into()978 .map_err(|_| "token uri is too long")?,978 .map_err(|_| "token uri is too long")?,pallets/nonfungible/src/lib.rsdiffbeforeafterboth101};101};102use up_data_structs::{102use up_data_structs::{103 AccessMode, CollectionId, CollectionFlags, CustomDataLimit, TokenId, CreateCollectionData,103 AccessMode, CollectionId, CollectionFlags, CustomDataLimit, TokenId, CreateCollectionData,104 CreateNftExData, mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission,104 CreateNftExData, mapping::TokenAddressMapping, budget::Budget, Property, PropertyKey,105 PropertyKey, PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty,105 PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild,106 TokenChild, AuxPropertyValue, PropertiesPermissionMap,106 AuxPropertyValue, PropertiesPermissionMap,107};107};108use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};108use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};109use pallet_common::{109use pallet_common::{110 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,110 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,111 eth::collection_id_to_address, erc::CollectionHelpersEvents,111 eth::collection_id_to_address,112};112};113use pallet_structure::{Pallet as PalletStructure, Error as StructureError};113use pallet_structure::{Pallet as PalletStructure, Error as StructureError};114use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};114use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};115use sp_core::{H160, Get};115use sp_core::H160;116use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};116use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};117use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};117use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};118use core::ops::Deref;118use core::ops::Deref;578 }578 }579579580 /// A batch operation to add, edit or remove properties for a token.580 /// A batch operation to add, edit or remove properties for a token.581 /// It sets or removes a token's properties according to582 /// `properties_updates` contents:583 /// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`584 /// * removes a property under the <key> if the value is `None` `(<key>, None)`.585 ///581 ///586 /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.582 /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.587 /// - `is_token_create`: Indicates that method is called during token initialization.583 /// - `is_token_create`: Indicates that method is called during token initialization.603 is_token_create: bool,599 is_token_create: bool,604 nesting_budget: &dyn Budget,600 nesting_budget: &dyn Budget,605 ) -> DispatchResult {601 ) -> DispatchResult {606 let mut collection_admin_status = None;607 let mut token_owner_result = None;608609 let mut is_collection_admin =610 || *collection_admin_status.get_or_insert_with(|| collection.is_owner_or_admin(sender));611612 let mut is_token_owner = || {602 let is_token_owner = || {613 *token_owner_result.get_or_insert_with(|| -> Result<bool, DispatchError> {614 let is_owned = <PalletStructure<T>>::check_indirectly_owned(603 let is_owned = <PalletStructure<T>>::check_indirectly_owned(615 sender.clone(),604 sender.clone(),616 collection.id,605 collection.id,620 )?;609 )?;621610622 Ok(is_owned)611 Ok(is_owned)623 })624 };612 };625613626 let mut stored_properties = <TokenProperties<T>>::get((collection.id, token_id));614 let stored_properties = <TokenProperties<T>>::get((collection.id, token_id));615627 let permissions = <PalletCommon<T>>::property_permissions(collection.id);616 <PalletCommon<T>>::modify_token_properties(628629 for (key, value) in properties_updates {617 collection,630 let permission = permissions631 .get(&key)632 .cloned()633 .unwrap_or_else(PropertyPermission::none);634635 let is_property_exists = stored_properties.get(&key).is_some();636637 match permission {638 PropertyPermission { mutable: false, .. } if is_property_exists => {618 sender,639 return Err(<CommonError<T>>::NoPermission.into());640 }641642 PropertyPermission {643 collection_admin,619 token_id,644 token_owner,620 properties_updates,645 ..646 } => {647 //TODO: investigate threats during public minting.648 if is_token_create && (collection_admin || token_owner) && value.is_some() {621 is_token_create,649 // Pass650 } else if collection_admin && is_collection_admin() {651 // Pass652 } else if token_owner && is_token_owner()? {653 // Pass654 } else {655 fail!(<CommonError<T>>::NoPermission);656 }657 }658 }659660 match value {661 Some(value) => {662 stored_properties663 .try_set(key.clone(), value)664 .map_err(<CommonError<T>>::from)?;665666 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(667 collection.id,668 token_id,669 key,670 ));671 }672 None => {673 stored_properties622 stored_properties,674 .remove(&key)675 .map_err(<CommonError<T>>::from)?;676677 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(678 collection.id,679 token_id,680 key,681 ));682 }683 }684685 <PalletEvm<T>>::deposit_log(686 CollectionHelpersEvents::TokenChanged {687 collection_id: collection_id_to_address(collection.id),688 token_id: token_id.into(),689 }690 .to_log(T::ContractAddress::get()),623 is_token_owner,691 );692 }693694 <TokenProperties<T>>::set((collection.id, token_id), stored_properties);624 |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),695625 )696 Ok(())697 }626 }698627699 /// Batch operation to add or edit properties for the token628 /// Batch operation to add or edit properties for the token1418 operator: &T::CrossAccountId,1347 operator: &T::CrossAccountId,1419 approve: bool,1348 approve: bool,1420 ) -> DispatchResult {1349 ) -> DispatchResult {1421 if collection.permissions.access() == AccessMode::AllowList {1350 <PalletCommon<T>>::set_allowance_for_all(1422 collection.check_allowlist(owner)?;1351 collection,1423 collection.check_allowlist(operator)?;1352 owner,1424 }1353 operator,14251354 approve,1426 <PalletCommon<T>>::ensure_correct_receiver(operator)?;14271428 // =========14291430 <CollectionAllowance<T>>::insert((collection.id, owner, operator), approve);1355 || <CollectionAllowance<T>>::insert((collection.id, owner, operator), approve),1431 <PalletEvm<T>>::deposit_log(1432 ERC721Events::ApprovalForAll {1356 ERC721Events::ApprovalForAll {1433 owner: *owner.as_eth(),1357 owner: *owner.as_eth(),1434 operator: *operator.as_eth(),1358 operator: *operator.as_eth(),1435 approved: approve,1359 approved: approve,1436 }1360 }1437 .to_log(collection_id_to_address(collection.id)),1361 .to_log(collection_id_to_address(collection.id)),1438 );1362 )1439 <PalletCommon<T>>::deposit_event(CommonEvent::ApprovedForAll(1440 collection.id,1441 owner.clone(),1442 operator.clone(),1443 approve,1444 ));1445 Ok(())1446 }1363 }144713641448 /// Tells whether the given `owner` approves the `operator`.1365 /// Tells whether the given `owner` approves the `operator`.pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth949 // /// @param tokens array of pairs of token ID and token URI for minted tokens949 // /// @param tokens array of pairs of token ID and token URI for minted tokens950 // /// @dev EVM selector for this function is: 0x36543006,950 // /// @dev EVM selector for this function is: 0x36543006,951 // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])951 // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])952 // function mintBulkWithTokenURI(address to, Tuple15[] memory tokens) public returns (bool) {952 // function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) public returns (bool) {953 // require(false, stub_error);953 // require(false, stub_error);954 // to;954 // to;955 // tokens;955 // tokens;981 }981 }982}982}983983984/// @dev anonymous struct984/// Data for creation token with uri.985struct Tuple15 {985struct TokenUri {986 /// Id of new token.986 uint256 field_0;987 uint256 id;988 /// Uri of new token.987 string field_1;989 string uri;988}990}989991990/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension992/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extensionpallets/refungible/CHANGELOG.mddiffbeforeafterboth445<!-- bureaucrate goes here -->5<!-- bureaucrate goes here -->667## [0.2.13] - 2023-02-0189### Added1011- The functions `allowanceCross` to `ERC20UniqueExtensions` interface.127## [0.2.12] - 2023-01-2013## [0.2.12] - 2023-01-208149### Fixed15### Fixedpallets/refungible/Cargo.tomldiffbeforeafterboth2edition = "2021"2edition = "2021"3license = "GPLv3"3license = "GPLv3"4name = "pallet-refungible"4name = "pallet-refungible"5version = "0.2.12"5version = "0.2.13"667[dependencies]7[dependencies]8# Note: `package = "parity-scale-codec"` must be supplied since the `Encode` macro searches for it.8# Note: `package = "parity-scale-codec"` must be supplied since the `Encode` macro searches for it.pallets/refungible/src/erc.rsdiffbeforeafterboth34 CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,34 CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,35 Error as CommonError,35 Error as CommonError,36 erc::{CommonEvmHandler, CollectionCall, static_property::key},36 erc::{CommonEvmHandler, CollectionCall, static_property::key},37 eth,37 eth::{self, TokenUri},38};38};39use pallet_evm::{account::CrossAccountId, PrecompileHandle};39use pallet_evm::{account::CrossAccountId, PrecompileHandle};40use pallet_evm_coder_substrate::{call, dispatch_to_evm};40use pallet_evm_coder_substrate::{call, dispatch_to_evm};999 &mut self,999 &mut self,1000 caller: Caller,1000 caller: Caller,1001 to: Address,1001 to: Address,1002 tokens: Vec<(U256, String)>,1002 tokens: Vec<TokenUri>,1003 ) -> Result<bool> {1003 ) -> Result<bool> {1004 let key = key::url();1004 let key = key::url();1005 let caller = T::CrossAccountId::from_eth(caller);1005 let caller = T::CrossAccountId::from_eth(caller);1017 .collect::<BTreeMap<_, _>>()1017 .collect::<BTreeMap<_, _>>()1018 .try_into()1018 .try_into()1019 .unwrap();1019 .unwrap();1020 for (id, token_uri) in tokens {1020 for TokenUri { id, uri } in tokens {1021 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;1021 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;1022 if id != expected_index {1022 if id != expected_index {1023 return Err("item id should be next".into());1023 return Err("item id should be next".into());1028 properties1028 properties1029 .try_push(Property {1029 .try_push(Property {1030 key: key.clone(),1030 key: key.clone(),1031 value: token_uri1031 value: uri1032 .into_bytes()1032 .into_bytes()1033 .try_into()1033 .try_into()1034 .map_err(|_| "token uri is too long")?,1034 .map_err(|_| "token uri is too long")?,pallets/refungible/src/erc_token.rsdiffbeforeafterboth31use pallet_common::{31use pallet_common::{32 CommonWeightInfo,32 CommonWeightInfo,33 erc::{CommonEvmHandler, PrecompileResult},33 erc::{CommonEvmHandler, PrecompileResult},34 eth::collection_id_to_address,34 eth::{collection_id_to_address, CrossAddress},35};35};36use pallet_evm::{account::CrossAccountId, PrecompileHandle};36use pallet_evm::{account::CrossAccountId, PrecompileHandle};37use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder};37use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder};203where203where204 T::AccountId: From<[u8; 32]>,204 T::AccountId: From<[u8; 32]>,205{205{206 /// @dev Function to check the amount of tokens that an owner allowed to a spender.207 /// @param owner crossAddress The address which owns the funds.208 /// @param spender crossAddress The address which will spend the funds.209 /// @return A uint256 specifying the amount of tokens still available for the spender.210 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {211 let owner = owner.into_sub_cross_account::<T>()?;212 let spender = spender.into_sub_cross_account::<T>()?;213214 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())215 }216206 /// @dev Function that burns an amount of the token of a given account,217 /// @dev Function that burns an amount of the token of a given account,207 /// deducting from the sender's allowance for said account.218 /// deducting from the sender's allowance for said account.230 fn burn_from_cross(241 fn burn_from_cross(231 &mut self,242 &mut self,232 caller: Caller,243 caller: Caller,233 from: pallet_common::eth::CrossAddress,244 from: CrossAddress,234 amount: U256,245 amount: U256,235 ) -> Result<bool> {246 ) -> Result<bool> {236 let caller = T::CrossAccountId::from_eth(caller);247 let caller = T::CrossAccountId::from_eth(caller);256 fn approve_cross(267 fn approve_cross(257 &mut self,268 &mut self,258 caller: Caller,269 caller: Caller,259 spender: pallet_common::eth::CrossAddress,270 spender: CrossAddress,260 amount: U256,271 amount: U256,261 ) -> Result<bool> {272 ) -> Result<bool> {262 let caller = T::CrossAccountId::from_eth(caller);273 let caller = T::CrossAccountId::from_eth(caller);286 fn transfer_cross(297 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {287 &mut self,288 caller: Caller,289 to: pallet_common::eth::CrossAddress,290 amount: U256,291 ) -> Result<bool> {292 let caller = T::CrossAccountId::from_eth(caller);298 let caller = T::CrossAccountId::from_eth(caller);293 let to = to.into_sub_cross_account::<T>()?;299 let to = to.into_sub_cross_account::<T>()?;309 fn transfer_from_cross(315 fn transfer_from_cross(310 &mut self,316 &mut self,311 caller: Caller,317 caller: Caller,312 from: pallet_common::eth::CrossAddress,318 from: CrossAddress,313 to: pallet_common::eth::CrossAddress,319 to: CrossAddress,314 amount: U256,320 amount: U256,315 ) -> Result<bool> {321 ) -> Result<bool> {316 let caller = T::CrossAccountId::from_eth(caller);322 let caller = T::CrossAccountId::from_eth(caller);pallets/refungible/src/lib.rsdiffbeforeafterboth929293use core::ops::Deref;93use core::ops::Deref;94use evm_coder::ToLog;94use evm_coder::ToLog;95use frame_support::{ensure, fail, storage::with_transaction, transactional};95use frame_support::{ensure, storage::with_transaction, transactional};96use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};96use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};97use pallet_evm_coder_substrate::WithRecorder;97use pallet_evm_coder_substrate::WithRecorder;98use pallet_common::{98use pallet_common::{99 CommonCollectionOperations, Error as CommonError, eth::collection_id_to_address,99 CommonCollectionOperations, Error as CommonError, eth::collection_id_to_address,100 Event as CommonEvent, Pallet as PalletCommon, erc::CollectionHelpersEvents,100 Event as CommonEvent, Pallet as PalletCommon,101};101};102use pallet_structure::Pallet as PalletStructure;102use pallet_structure::Pallet as PalletStructure;103use sp_core::{Get, H160};103use sp_core::H160;104use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};104use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};105use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};105use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};106use up_data_structs::{106use up_data_structs::{107 AccessMode, budget::Budget, CollectionId, CollectionFlags, CreateCollectionData,107 AccessMode, budget::Budget, CollectionId, CollectionFlags, CreateCollectionData,108 mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, Property, PropertyKey,108 mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, Property, PropertyKey,109 PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, TokenId,109 PropertyKeyPermission, PropertyScope, PropertyValue, TokenId, TrySetProperty,110 TrySetProperty, PropertiesPermissionMap, CreateRefungibleExMultipleOwners, TokenOwnerError,110 PropertiesPermissionMap, CreateRefungibleExMultipleOwners, TokenOwnerError,111};111};112112240 QueryKind = ValueQuery,240 QueryKind = ValueQuery,241 >;241 >;242242243 /// Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet.243 /// Spender set by a wallet owner that could perform certain transactions on all tokens in the wallet.244 #[pallet::storage]244 #[pallet::storage]245 pub type CollectionAllowance<T: Config> = StorageNMap<245 pub type CollectionAllowance<T: Config> = StorageNMap<246 Key = (246 Key = (247 Key<Twox64Concat, CollectionId>,247 Key<Twox64Concat, CollectionId>,248 Key<Blake2_128Concat, T::CrossAccountId>,248 Key<Blake2_128Concat, T::CrossAccountId>, // Owner249 Key<Blake2_128Concat, T::CrossAccountId>,249 Key<Blake2_128Concat, T::CrossAccountId>, // Spender250 ),250 ),251 Value = bool,251 Value = bool,252 QueryKind = ValueQuery,252 QueryKind = ValueQuery,541 is_token_create: bool,541 is_token_create: bool,542 nesting_budget: &dyn Budget,542 nesting_budget: &dyn Budget,543 ) -> DispatchResult {543 ) -> DispatchResult {544 let is_collection_admin = || collection.is_owner_or_admin(sender);545 let is_token_owner = || -> Result<bool, DispatchError> {544 let is_token_owner = || -> Result<bool, DispatchError> {546 let balance = collection.balance(sender.clone(), token_id);545 let balance = collection.balance(sender.clone(), token_id);547 let total_pieces: u128 =546 let total_pieces: u128 =561 Ok(is_bundle_owner)560 Ok(is_bundle_owner)562 };561 };563562564 let mut stored_properties = <TokenProperties<T>>::get((collection.id, token_id));563 let stored_properties = <TokenProperties<T>>::get((collection.id, token_id));564565 let permissions = <PalletCommon<T>>::property_permissions(collection.id);565 <PalletCommon<T>>::modify_token_properties(566567 for (key, value) in properties_updates {566 collection,568 let permission = permissions569 .get(&key)570 .cloned()571 .unwrap_or_else(PropertyPermission::none);572573 let is_property_exists = stored_properties.get(&key).is_some();574575 match permission {576 PropertyPermission { mutable: false, .. } if is_property_exists => {567 sender,577 return Err(<CommonError<T>>::NoPermission.into());578 }579580 PropertyPermission {581 collection_admin,568 token_id,582 token_owner,569 properties_updates,583 ..584 } => {585 //TODO: investigate threats during public minting.586 let is_token_create =570 is_token_create,587 is_token_create && (collection_admin || token_owner) && value.is_some();588 if !(is_token_create589 || (collection_admin && is_collection_admin())590 || (token_owner && is_token_owner()?))591 {592 fail!(<CommonError<T>>::NoPermission);593 }594 }595 }596597 match value {598 Some(value) => {599 stored_properties600 .try_set(key.clone(), value)601 .map_err(<CommonError<T>>::from)?;602603 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(604 collection.id,605 token_id,606 key,607 ));608 }609 None => {610 stored_properties571 stored_properties,611 .remove(&key)612 .map_err(<CommonError<T>>::from)?;613614 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(615 collection.id,616 token_id,617 key,618 ));619 }620 }621622 <PalletEvm<T>>::deposit_log(623 CollectionHelpersEvents::TokenChanged {624 collection_id: collection_id_to_address(collection.id),625 token_id: token_id.into(),626 }627 .to_log(T::ContractAddress::get()),572 is_token_owner,628 );629 }630631 <TokenProperties<T>>::set((collection.id, token_id), stored_properties);573 |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),632574 )633 Ok(())634 }575 }635576636 pub fn set_token_properties(577 pub fn set_token_properties(1462 pub fn set_allowance_for_all(1403 pub fn set_allowance_for_all(1463 collection: &RefungibleHandle<T>,1404 collection: &RefungibleHandle<T>,1464 owner: &T::CrossAccountId,1405 owner: &T::CrossAccountId,1465 operator: &T::CrossAccountId,1406 spender: &T::CrossAccountId,1466 approve: bool,1407 approve: bool,1467 ) -> DispatchResult {1408 ) -> DispatchResult {1409 <PalletCommon<T>>::set_allowance_for_all(1468 if collection.permissions.access() == AccessMode::AllowList {1410 collection,1469 collection.check_allowlist(owner)?;1411 owner,1470 collection.check_allowlist(operator)?;1412 spender,1471 }1413 approve,14721473 <PalletCommon<T>>::ensure_correct_receiver(operator)?;1414 || <CollectionAllowance<T>>::insert((collection.id, owner, spender), approve),14741475 // =========14761477 <CollectionAllowance<T>>::insert((collection.id, owner, operator), approve);1478 <PalletEvm<T>>::deposit_log(1479 ERC721Events::ApprovalForAll {1415 ERC721Events::ApprovalForAll {1480 owner: *owner.as_eth(),1416 owner: *owner.as_eth(),1481 operator: *operator.as_eth(),1417 operator: *spender.as_eth(),1482 approved: approve,1418 approved: approve,1483 }1419 }1484 .to_log(collection_id_to_address(collection.id)),1420 .to_log(collection_id_to_address(collection.id)),1485 );1421 )1486 <PalletCommon<T>>::deposit_event(CommonEvent::ApprovedForAll(1487 collection.id,1488 owner.clone(),1489 operator.clone(),1490 approve,1491 ));1492 Ok(())1493 }1422 }149414231495 /// Tells whether the given `owner` approves the `operator`.1424 /// Tells whether the given `owner` approves the `operator`.1496 pub fn allowance_for_all(1425 pub fn allowance_for_all(1497 collection: &RefungibleHandle<T>,1426 collection: &RefungibleHandle<T>,1498 owner: &T::CrossAccountId,1427 owner: &T::CrossAccountId,1499 operator: &T::CrossAccountId,1428 spender: &T::CrossAccountId,1500 ) -> bool {1429 ) -> bool {1501 <CollectionAllowance<T>>::get((collection.id, owner, operator))1430 <CollectionAllowance<T>>::get((collection.id, owner, spender))1502 }1431 }150314321504 pub fn repair_item(collection: &RefungibleHandle<T>, token: TokenId) -> DispatchResult {1433 pub fn repair_item(collection: &RefungibleHandle<T>, token: TokenId) -> DispatchResult {pallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth938 // /// @param tokens array of pairs of token ID and token URI for minted tokens938 // /// @param tokens array of pairs of token ID and token URI for minted tokens939 // /// @dev EVM selector for this function is: 0x36543006,939 // /// @dev EVM selector for this function is: 0x36543006,940 // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])940 // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])941 // function mintBulkWithTokenURI(address to, Tuple14[] memory tokens) public returns (bool) {941 // function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) public returns (bool) {942 // require(false, stub_error);942 // require(false, stub_error);943 // to;943 // to;944 // tokens;944 // tokens;982 }982 }983}983}984984985/// @dev anonymous struct985/// Data for creation token with uri.986struct Tuple14 {986struct TokenUri {987 /// Id of new token.987 uint256 field_0;988 uint256 id;989 /// Uri of new token.988 string field_1;990 string uri;989}991}990992991/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension993/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extensionpallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungibleToken.soldiffbeforeafterboth36 }36 }37}37}383839/// @dev the ERC-165 identifier for this interface is 0xe17a7d2b39/// @dev the ERC-165 identifier for this interface is 0x01d536fc40contract ERC20UniqueExtensions is Dummy, ERC165 {40contract ERC20UniqueExtensions is Dummy, ERC165 {41 /// @dev Function to check the amount of tokens that an owner allowed to a spender.42 /// @param owner crossAddress The address which owns the funds.43 /// @param spender crossAddress The address which will spend the funds.44 /// @return A uint256 specifying the amount of tokens still available for the spender.45 /// @dev EVM selector for this function is: 0xe0af4bd7,46 /// or in textual repr: allowanceCross((address,uint256),(address,uint256))47 function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) public view returns (uint256) {48 require(false, stub_error);49 owner;50 spender;51 dummy;52 return 0;53 }5441 // /// @dev Function that burns an amount of the token of a given account,55 // /// @dev Function that burns an amount of the token of a given account,42 // /// deducting from the sender's allowance for said account.56 // /// deducting from the sender's allowance for said account.pallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterbothbinary blob — no preview
runtime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth341 ERC165Call(_, _) => None,341 ERC165Call(_, _) => None,342342343 // Not sponsored343 // Not sponsored344 BurnFrom { .. } | BurnFromCross { .. } | Repartition { .. } => None,344 AllowanceCross { .. } | BurnFrom { .. } | BurnFromCross { .. } | Repartition { .. } => {345 None346 }345347346 TransferCross { .. } | TransferFromCross { .. } => {348 TransferCross { .. } | TransferFromCross { .. } => {347 let RefungibleTokenHandle(handle, token_id) = token;349 let RefungibleTokenHandle(handle, token_id) = token;tests/src/eth/abi/fungible.jsondiffbeforeafterboth93 "stateMutability": "view",93 "stateMutability": "view",94 "type": "function"94 "type": "function"95 },95 },96 {97 "inputs": [98 {99 "components": [100 { "internalType": "address", "name": "eth", "type": "address" },101 { "internalType": "uint256", "name": "sub", "type": "uint256" }102 ],103 "internalType": "struct CrossAddress",104 "name": "owner",105 "type": "tuple"106 },107 {108 "components": [109 { "internalType": "address", "name": "eth", "type": "address" },110 { "internalType": "uint256", "name": "sub", "type": "uint256" }111 ],112 "internalType": "struct CrossAddress",113 "name": "spender",114 "type": "tuple"115 }116 ],117 "name": "allowanceCross",118 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],119 "stateMutability": "view",120 "type": "function"121 },96 {122 {97 "inputs": [123 "inputs": [98 {124 {408 "inputs": [434 "inputs": [409 {435 {410 "components": [436 "components": [411 { "internalType": "address", "name": "field_0", "type": "address" },437 { "internalType": "address", "name": "to", "type": "address" },412 { "internalType": "uint256", "name": "field_1", "type": "uint256" }438 { "internalType": "uint256", "name": "amount", "type": "uint256" }413 ],439 ],414 "internalType": "struct Tuple9[]",440 "internalType": "struct AmountForAddress[]",415 "name": "amounts",441 "name": "amounts",416 "type": "tuple[]"442 "type": "tuple[]"417 }443 }tests/src/eth/abi/reFungibleToken.jsondiffbeforeafterboth59 "stateMutability": "view",59 "stateMutability": "view",60 "type": "function"60 "type": "function"61 },61 },62 {63 "inputs": [64 {65 "components": [66 { "internalType": "address", "name": "eth", "type": "address" },67 { "internalType": "uint256", "name": "sub", "type": "uint256" }68 ],69 "internalType": "struct CrossAddress",70 "name": "owner",71 "type": "tuple"72 },73 {74 "components": [75 { "internalType": "address", "name": "eth", "type": "address" },76 { "internalType": "uint256", "name": "sub", "type": "uint256" }77 ],78 "internalType": "struct CrossAddress",79 "name": "spender",80 "type": "tuple"81 }82 ],83 "name": "allowanceCross",84 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],85 "stateMutability": "view",86 "type": "function"87 },62 {88 {63 "inputs": [89 "inputs": [64 { "internalType": "address", "name": "spender", "type": "address" },90 { "internalType": "address", "name": "spender", "type": "address" },tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth353 bytes value;353 bytes value;354}354}355355356/// @dev the ERC-165 identifier for this interface is 0x65789571356/// @dev the ERC-165 identifier for this interface is 0x85d7dea6357interface ERC20UniqueExtensions is Dummy, ERC165 {357interface ERC20UniqueExtensions is Dummy, ERC165 {358 /// @dev Function to check the amount of tokens that an owner allowed to a spender.359 /// @param owner crossAddress The address which owns the funds.360 /// @param spender crossAddress The address which will spend the funds.361 /// @return A uint256 specifying the amount of tokens still available for the spender.362 /// @dev EVM selector for this function is: 0xe0af4bd7,363 /// or in textual repr: allowanceCross((address,uint256),(address,uint256))364 function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256);365358 /// @notice A description for the collection.366 /// @notice A description for the collection.359 /// @dev EVM selector for this function is: 0x7284e416,367 /// @dev EVM selector for this function is: 0x7284e416,390 /// @param amounts array of pairs of account address and amount398 /// @param amounts array of pairs of account address and amount391 /// @dev EVM selector for this function is: 0x1acf2d55,399 /// @dev EVM selector for this function is: 0x1acf2d55,392 /// or in textual repr: mintBulk((address,uint256)[])400 /// or in textual repr: mintBulk((address,uint256)[])393 function mintBulk(Tuple9[] memory amounts) external returns (bool);401 function mintBulk(AmountForAddress[] memory amounts) external returns (bool);394402395 /// @dev EVM selector for this function is: 0x2ada85ff,403 /// @dev EVM selector for this function is: 0x2ada85ff,396 /// or in textual repr: transferCross((address,uint256),uint256)404 /// or in textual repr: transferCross((address,uint256),uint256)410 function collectionHelperAddress() external view returns (address);418 function collectionHelperAddress() external view returns (address);411}419}412420413/// @dev anonymous struct414struct Tuple9 {421struct AmountForAddress {415 address field_0;422 address to;416 uint256 field_1;423 uint256 amount;417}424}418425419/// @dev the ERC-165 identifier for this interface is 0x40c10f19426/// @dev the ERC-165 identifier for this interface is 0x40c10f19tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth644 // /// @param tokens array of pairs of token ID and token URI for minted tokens644 // /// @param tokens array of pairs of token ID and token URI for minted tokens645 // /// @dev EVM selector for this function is: 0x36543006,645 // /// @dev EVM selector for this function is: 0x36543006,646 // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])646 // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])647 // function mintBulkWithTokenURI(address to, Tuple13[] memory tokens) external returns (bool);647 // function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool);648648649 /// @notice Function to mint a token.649 /// @notice Function to mint a token.650 /// @param to The new owner crossAccountId650 /// @param to The new owner crossAccountId660 function collectionHelperAddress() external view returns (address);660 function collectionHelperAddress() external view returns (address);661}661}662662663/// @dev anonymous struct663/// Data for creation token with uri.664struct Tuple13 {664struct TokenUri {665 /// Id of new token.665 uint256 field_0;666 uint256 id;667 /// Uri of new token.666 string field_1;668 string uri;667}669}668670669/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension671/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extensiontests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth638 // /// @param tokens array of pairs of token ID and token URI for minted tokens638 // /// @param tokens array of pairs of token ID and token URI for minted tokens639 // /// @dev EVM selector for this function is: 0x36543006,639 // /// @dev EVM selector for this function is: 0x36543006,640 // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])640 // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])641 // function mintBulkWithTokenURI(address to, Tuple12[] memory tokens) external returns (bool);641 // function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool);642642643 /// @notice Function to mint a token.643 /// @notice Function to mint a token.644 /// @param to The new owner crossAccountId644 /// @param to The new owner crossAccountId661 function collectionHelperAddress() external view returns (address);661 function collectionHelperAddress() external view returns (address);662}662}663663664/// @dev anonymous struct664/// Data for creation token with uri.665struct Tuple12 {665struct TokenUri {666 /// Id of new token.666 uint256 field_0;667 uint256 id;668 /// Uri of new token.667 string field_1;669 string uri;668}670}669671670/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension672/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extensiontests/src/eth/api/UniqueRefungibleToken.soldiffbeforeafterboth23 function parentTokenId() external view returns (uint256);23 function parentTokenId() external view returns (uint256);24}24}252526/// @dev the ERC-165 identifier for this interface is 0xe17a7d2b26/// @dev the ERC-165 identifier for this interface is 0x01d536fc27interface ERC20UniqueExtensions is Dummy, ERC165 {27interface ERC20UniqueExtensions is Dummy, ERC165 {28 /// @dev Function to check the amount of tokens that an owner allowed to a spender.29 /// @param owner crossAddress The address which owns the funds.30 /// @param spender crossAddress The address which will spend the funds.31 /// @return A uint256 specifying the amount of tokens still available for the spender.32 /// @dev EVM selector for this function is: 0xe0af4bd7,33 /// or in textual repr: allowanceCross((address,uint256),(address,uint256))34 function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256);3528 // /// @dev Function that burns an amount of the token of a given account,36 // /// @dev Function that burns an amount of the token of a given account,29 // /// deducting from the sender's allowance for said account.37 // /// deducting from the sender's allowance for said account.tests/src/eth/fungible.test.tsdiffbeforeafterboth134 const allowance = await contract.methods.allowance(owner, spender).call();134 const allowance = await contract.methods.allowance(owner, spender).call();135 expect(+allowance).to.equal(100);135 expect(+allowance).to.equal(100);136 }136 }137 {138 const ownerCross = helper.ethCrossAccount.fromAddress(owner);139 const spenderCross = helper.ethCrossAccount.fromAddress(spender);140 const allowance = await contract.methods.allowanceCross(ownerCross, spenderCross).call();141 expect(+allowance).to.equal(100);142 }137 });143 });138144139 itEth('Can perform approveCross()', async ({helper}) => {145 itEth('Can perform approveCross()', async ({helper}) => {tests/src/eth/proxy/UniqueNFTProxy.soldiffbeforeafterboth168 return proxied.mintBulk(to, tokenIds);168 return proxied.mintBulk(to, tokenIds);169 }169 }170170171 function mintBulkWithTokenURI(address to, Tuple6[] memory tokens)171 function mintBulkWithTokenURI(address to, TokenUri[] memory tokens)172 external172 external173 override173 override174 returns (bool)174 returns (bool)tests/src/eth/reFungible.test.tsdiffbeforeafterboth215215216 const rftToken = await helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner, true);216 const rftToken = await helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner, true);217217218 {218 {219 await rftToken.methods.approve(operator, 15n).send({from: owner});219 await rftToken.methods.approve(operator, 15n).send({from: owner});220 await contract.methods.setApprovalForAll(operator, true).send({from: owner});220 await contract.methods.setApprovalForAll(operator, true).send({from: owner});221 await rftToken.methods.burnFrom(owner, 10n).send({from: operator});221 await rftToken.methods.burnFrom(owner, 10n).send({from: operator});222 }223 {222 const allowance = await rftToken.methods.allowance(owner, operator).call();224 const allowance = await rftToken.methods.allowance(owner, operator).call();223 expect(allowance).to.be.equal('5');225 expect(+allowance).to.be.equal(5);224 }226 }227 {228 const ownerCross = helper.ethCrossAccount.fromAddress(owner);229 const operatorCross = helper.ethCrossAccount.fromAddress(operator);230 const allowance = await rftToken.methods.allowanceCross(ownerCross, operatorCross).call();231 expect(+allowance).to.equal(5);232 }225 });233 });226234227 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {235 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {