From d8b03ef62dde290802b745cacc5f15ae72f3ab6a Mon Sep 17 00:00:00 2001 From: ut-akuznetsov <59873862+ut-akuznetsov@users.noreply.github.com> Date: Fri, 16 Dec 2022 18:36:04 +0000 Subject: [PATCH] Merge pull request #743 from UniqueNetwork/feature/mintCross added `mintCross` function for `UniqueExtensoins` interfaces --- --- a/Cargo.lock +++ b/Cargo.lock @@ -6085,7 +6085,7 @@ [[package]] name = "pallet-fungible" -version = "0.1.7" +version = "0.1.9" dependencies = [ "ethereum 0.14.0", "evm-coder", @@ -6342,7 +6342,7 @@ [[package]] name = "pallet-nonfungible" -version = "0.1.11" +version = "0.1.12" dependencies = [ "ethereum 0.14.0", "evm-coder", @@ -6501,7 +6501,7 @@ [[package]] name = "pallet-refungible" -version = "0.2.10" +version = "0.2.11" dependencies = [ "derivative", "ethereum 0.14.0", --- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -36,7 +36,7 @@ use crate::{ Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf, eth::{ - EthCrossAccount, convert_cross_account_to_uint256, CollectionPermissions as EvmPermissions, + EthCrossAccount, CollectionPermissions as EvmPermissions, CollectionLimits as EvmCollectionLimits, }, weights::WeightInfo, @@ -289,20 +289,13 @@ /// Get current sponsor. /// /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - fn collection_sponsor(&self) -> Result<(address, uint256)> { + fn collection_sponsor(&self) -> Result { let sponsor = match self.collection.sponsorship.sponsor() { Some(sponsor) => sponsor, None => return Ok(Default::default()), }; - let sponsor = T::CrossAccountId::from_sub(sponsor.clone()); - let result: (address, uint256) = if sponsor.is_canonical_substrate() { - let sponsor = convert_cross_account_to_uint256::(&sponsor); - (Default::default(), sponsor) - } else { - let sponsor = *sponsor.as_eth(); - (sponsor, Default::default()) - }; - Ok(result) + + Ok(EthCrossAccount::from_sub::(&sponsor)) } /// Get current collection limits. --- a/pallets/common/src/eth.rs +++ b/pallets/common/src/eth.rs @@ -53,15 +53,6 @@ address[0..16] == ETH_COLLECTION_PREFIX } -/// Convert `CrossAccountId` to `uint256`. -pub fn convert_cross_account_to_uint256(from: &T::CrossAccountId) -> uint256 -where - T::AccountId: AsRef<[u8; 32]>, -{ - let slice = from.as_sub().as_ref(); - uint256::from_big_endian(slice) -} - /// Convert `uint256` to `CrossAccountId`. pub fn convert_uint256_to_cross_account(from: uint256) -> T::CrossAccountId where @@ -71,46 +62,6 @@ from.to_big_endian(&mut new_admin_arr); let account_id = T::AccountId::from(new_admin_arr); T::CrossAccountId::from_sub(account_id) -} - -/// Convert `CrossAccountId` to `(address, uint256)`. -pub fn convert_cross_account_to_tuple( - cross_account_id: &T::CrossAccountId, -) -> (address, uint256) -where - T::AccountId: AsRef<[u8; 32]>, -{ - if cross_account_id.is_canonical_substrate() { - let sub = convert_cross_account_to_uint256::(cross_account_id); - (Default::default(), sub) - } else { - let eth = *cross_account_id.as_eth(); - (eth, Default::default()) - } -} - -/// Convert tuple `(address, uint256)` to `CrossAccountId`. -/// -/// If `address` in the tuple has *default* value, then the canonical form is substrate, -/// if `uint256` has *default* value, then the ethereum form is canonical, -/// if both values are *default* or *non default*, then this is considered an invalid address and `Error` is returned. -pub fn convert_tuple_to_cross_account( - eth_cross_account_id: (address, uint256), -) -> evm_coder::execution::Result -where - T::AccountId: From<[u8; 32]>, -{ - if eth_cross_account_id == Default::default() { - Err("All fields of cross account is zeroed".into()) - } else if eth_cross_account_id.0 == Default::default() { - Ok(convert_uint256_to_cross_account::( - eth_cross_account_id.1, - )) - } else if eth_cross_account_id.1 == Default::default() { - Ok(T::CrossAccountId::from_eth(eth_cross_account_id.0)) - } else { - Err("All fields of cross account is non zeroed".into()) - } } /// Cross account struct @@ -121,16 +72,14 @@ } impl EthCrossAccount { + /// Converts `CrossAccountId` to `EthCrossAccountId` pub fn from_sub_cross_account(cross_account_id: &T::CrossAccountId) -> Self where T: pallet_evm::Config, T::AccountId: AsRef<[u8; 32]>, { if cross_account_id.is_canonical_substrate() { - Self { - eth: Default::default(), - sub: convert_cross_account_to_uint256::(cross_account_id), - } + Self::from_sub::(cross_account_id.as_sub()) } else { Self { eth: *cross_account_id.as_eth(), @@ -138,7 +87,18 @@ } } } - + /// Creates `EthCrossAccount` from substrate account + pub fn from_sub(account_id: &T::AccountId) -> Self + where + T: pallet_evm::Config, + T::AccountId: AsRef<[u8; 32]>, + { + Self { + eth: Default::default(), + sub: uint256::from_big_endian(account_id.as_ref()), + } + } + /// Converts `EthCrossAccount` to `CrossAccountId` pub fn into_sub_cross_account(&self) -> evm_coder::execution::Result where T: pallet_evm::Config, @@ -163,29 +123,41 @@ /// How many tokens can a user have on one account. #[default] AccountTokenOwnership, + /// How many bytes of data are available for sponsorship. SponsoredDataSize, + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] SponsoredDataRateLimit, + /// How many tokens can be mined into this collection. TokenLimit, + /// Timeouts for transfer sponsoring. SponsorTransferTimeout, + /// Timeout for sponsoring an approval in passed blocks. SponsorApproveTimeout, + /// Whether the collection owner of the collection can send tokens (which belong to other users). OwnerCanTransfer, + /// Can the collection owner burn other people's tokens. OwnerCanDestroy, + /// Is it possible to send tokens from this collection between users. TransferEnabled, } +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. #[derive(Default, Debug, Clone, Copy, AbiCoder)] #[repr(u8)] pub enum CollectionPermissions { + /// Owner of token can nest tokens under it. #[default] - CollectionAdmin, TokenOwner, + + /// Admin of token collection can nest tokens under token. + CollectionAdmin, } /// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. --- a/pallets/evm-contract-helpers/Cargo.toml +++ b/pallets/evm-contract-helpers/Cargo.toml @@ -50,6 +50,7 @@ "pallet-evm-coder-substrate/std", "pallet-evm/std", "up-sponsorship/std", + "pallet-common/std", ] try-runtime = ["frame-support/try-runtime"] -stubgen = ["evm-coder/stubgen"] +stubgen = ["evm-coder/stubgen", "pallet-common/stubgen"] --- a/pallets/evm-contract-helpers/src/eth.rs +++ b/pallets/evm-contract-helpers/src/eth.rs @@ -25,6 +25,7 @@ types::*, ToLog, }; +use pallet_common::eth::EthCrossAccount; use pallet_evm::{ ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle, account::CrossAccountId, @@ -174,11 +175,9 @@ /// /// @param contractAddress The contract for which a sponsor is requested. /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - fn sponsor(&self, contract_address: address) -> Result<(address, uint256)> { - let sponsor = - Pallet::::get_sponsor(contract_address).ok_or("Contract has no sponsor")?; - Ok(pallet_common::eth::convert_cross_account_to_tuple::( - &sponsor, + fn sponsor(&self, contract_address: address) -> Result { + Ok(EthCrossAccount::from_sub_cross_account::( + &Pallet::::get_sponsor(contract_address).ok_or("Contract has no sponsor")?, )) } --- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol +++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol @@ -96,11 +96,11 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x766c4f37, /// or in textual repr: sponsor(address) - function sponsor(address contractAddress) public view returns (Tuple0 memory) { + function sponsor(address contractAddress) public view returns (EthCrossAccount memory) { require(false, stub_error); contractAddress; dummy; - return Tuple0(0x0000000000000000000000000000000000000000, 0); + return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); } /// Check tat contract has confirmed sponsor. @@ -265,8 +265,8 @@ } } -/// @dev anonymous struct -struct Tuple0 { - address field_0; - uint256 field_1; +/// @dev Cross account struct +struct EthCrossAccount { + address eth; + uint256 sub; } --- a/pallets/fungible/CHANGELOG.md +++ b/pallets/fungible/CHANGELOG.md @@ -4,6 +4,12 @@ +## [0.1.9] - 2022-12-01 + +### Added + +- The functions `mintCross` to `ERC20UniqueExtensions` interface. + ## [0.1.8] - 2022-11-18 ### Added --- a/pallets/fungible/Cargo.toml +++ b/pallets/fungible/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-fungible" -version = "0.1.7" +version = "0.1.9" license = "GPLv3" edition = "2021" --- a/pallets/fungible/src/erc.rs +++ b/pallets/fungible/src/erc.rs @@ -174,6 +174,19 @@ .collect::()) } + #[weight(>::create_item())] + fn mint_cross(&mut self, caller: caller, to: EthCrossAccount, amount: uint256) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let to = to.into_sub_cross_account::()?; + let amount = amount.try_into().map_err(|_| "amount overflow")?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + >::create_item(&self, &caller, (to, amount), &budget) + .map_err(dispatch_to_evm::)?; + Ok(true) + } + #[weight(>::approve())] fn approve_cross( &mut self, --- a/pallets/fungible/src/stubs/UniqueFungible.sol +++ b/pallets/fungible/src/stubs/UniqueFungible.sol @@ -152,10 +152,10 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() public view returns (Tuple8 memory) { + function collectionSponsor() public view returns (EthCrossAccount memory) { require(false, stub_error); dummy; - return Tuple8(0x0000000000000000000000000000000000000000, 0); + return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); } /// Get current collection limits. @@ -173,10 +173,10 @@ /// Return `false` if a limit not set. /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() public view returns (Tuple20[] memory) { + function collectionLimits() public view returns (Tuple23[] memory) { require(false, stub_error); dummy; - return new Tuple20[](0); + return new Tuple23[](0); } /// Set limits for the collection. @@ -284,19 +284,19 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() public view returns (Tuple26 memory) { + function collectionNestingRestrictedCollectionIds() public view returns (Tuple29 memory) { require(false, stub_error); dummy; - return Tuple26(false, new uint256[](0)); + return Tuple29(false, new uint256[](0)); } /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() public view returns (Tuple29[] memory) { + function collectionNestingPermissions() public view returns (Tuple32[] memory) { require(false, stub_error); dummy; - return new Tuple29[](0); + return new Tuple32[](0); } /// Set the collection access method. @@ -476,13 +476,13 @@ } /// @dev anonymous struct -struct Tuple29 { +struct Tuple32 { CollectionPermissions field_0; bool field_1; } /// @dev anonymous struct -struct Tuple26 { +struct Tuple29 { bool field_0; uint256[] field_1; } @@ -510,7 +510,7 @@ } /// @dev anonymous struct -struct Tuple20 { +struct Tuple23 { CollectionLimits field_0; bool field_1; uint256 field_2; @@ -522,7 +522,7 @@ bytes value; } -/// @dev the ERC-165 identifier for this interface is 0x5b7038cf +/// @dev the ERC-165 identifier for this interface is 0x7dee5997 contract ERC20UniqueExtensions is Dummy, ERC165 { /// @notice A description for the collection. /// @dev EVM selector for this function is: 0x7284e416, @@ -533,6 +533,16 @@ return ""; } + /// @dev EVM selector for this function is: 0x269e6158, + /// or in textual repr: mintCross((address,uint256),uint256) + function mintCross(EthCrossAccount memory to, uint256 amount) public returns (bool) { + require(false, stub_error); + to; + amount; + dummy = 0; + return false; + } + /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) function approveCross(EthCrossAccount memory spender, uint256 amount) public returns (bool) { @@ -577,7 +587,7 @@ /// @param amounts array of pairs of account address and amount /// @dev EVM selector for this function is: 0x1acf2d55, /// or in textual repr: mintBulk((address,uint256)[]) - function mintBulk(Tuple8[] memory amounts) public returns (bool) { + function mintBulk(Tuple9[] memory amounts) public returns (bool) { require(false, stub_error); amounts; dummy = 0; @@ -611,7 +621,7 @@ } /// @dev anonymous struct -struct Tuple8 { +struct Tuple9 { address field_0; uint256 field_1; } --- a/pallets/nonfungible/CHANGELOG.md +++ b/pallets/nonfungible/CHANGELOG.md @@ -4,7 +4,7 @@ -## [0.1.11] - 2022-12-16 +## [0.1.12] - 2022-12-16 ### Added @@ -14,6 +14,12 @@ - Hide `setTokenPropertyPermission` function in `TokenProperties` interface. +## [0.1.11] - 2022-12-01 + +### Added + +- The functions `mintCross` to `ERC721UniqueExtensions` interface. + ## [0.1.10] - 2022-11-18 ### Added --- a/pallets/nonfungible/Cargo.toml +++ b/pallets/nonfungible/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-nonfungible" -version = "0.1.11" +version = "0.1.12" license = "GPLv3" edition = "2021" --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -605,7 +605,7 @@ Ok(false) } - /// @notice Function to mint token. + /// @notice Function to mint a token. /// @param to The new owner /// @return uint256 The id of the newly minted token #[weight(>::create_item())] @@ -618,7 +618,7 @@ Ok(token_id) } - /// @notice Function to mint token. + /// @notice Function to mint a token. /// @dev `tokenId` should be obtained with `nextTokenId` method, /// unlike standard, you can't specify it manually /// @param to The new owner @@ -1069,6 +1069,58 @@ .map_err(dispatch_to_evm::)?; Ok(true) } + + /// @notice Function to mint a token. + /// @param to The new owner crossAccountId + /// @param properties Properties of minted token + /// @return uint256 The id of the newly minted token + #[weight(>::create_item())] + fn mint_cross( + &mut self, + caller: caller, + to: EthCrossAccount, + properties: Vec, + ) -> Result { + let token_id = >::get(self.id) + .checked_add(1) + .ok_or("item id overflow")?; + + let to = to.into_sub_cross_account::()?; + + let properties = properties + .into_iter() + .map(|PropertyStruct { key, value }| { + let key = >::from(key) + .try_into() + .map_err(|_| "key too large")?; + + let value = value.0.try_into().map_err(|_| "value too large")?; + + Ok(Property { key, value }) + }) + .collect::>>()? + .try_into() + .map_err(|_| Error::Revert(alloc::format!("too many properties")))?; + + let caller = T::CrossAccountId::from_eth(caller); + + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + >::create_item( + self, + &caller, + CreateItemData:: { + properties, + owner: to, + }, + &budget, + ) + .map_err(dispatch_to_evm::)?; + + Ok(token_id.into()) + } } #[solidity_interface( --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -42,7 +42,7 @@ /// @param permissions Permissions for keys. /// @dev EVM selector for this function is: 0xbd92983a, /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(Tuple59[] memory permissions) public { + function setTokenPropertyPermissions(Tuple61[] memory permissions) public { require(false, stub_error); permissions; dummy = 0; @@ -51,10 +51,10 @@ /// @notice Get permissions for token properties. /// @dev EVM selector for this function is: 0xf23d7790, /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() public view returns (Tuple59[] memory) { + function tokenPropertyPermissions() public view returns (Tuple61[] memory) { require(false, stub_error); dummy; - return new Tuple59[](0); + return new Tuple61[](0); } // /// @notice Set token property value. @@ -144,13 +144,13 @@ } /// @dev anonymous struct -struct Tuple59 { +struct Tuple61 { string field_0; - Tuple57[] field_1; + Tuple59[] field_1; } /// @dev anonymous struct -struct Tuple57 { +struct Tuple59 { EthTokenPermissions field_0; bool field_1; } @@ -290,10 +290,10 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() public view returns (Tuple30 memory) { + function collectionSponsor() public view returns (EthCrossAccount memory) { require(false, stub_error); dummy; - return Tuple30(0x0000000000000000000000000000000000000000, 0); + return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); } /// Get current collection limits. @@ -311,10 +311,10 @@ /// Return `false` if a limit not set. /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() public view returns (Tuple33[] memory) { + function collectionLimits() public view returns (Tuple35[] memory) { require(false, stub_error); dummy; - return new Tuple33[](0); + return new Tuple35[](0); } /// Set limits for the collection. @@ -422,19 +422,19 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() public view returns (Tuple39 memory) { + function collectionNestingRestrictedCollectionIds() public view returns (Tuple41 memory) { require(false, stub_error); dummy; - return Tuple39(false, new uint256[](0)); + return Tuple41(false, new uint256[](0)); } /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() public view returns (Tuple42[] memory) { + function collectionNestingPermissions() public view returns (Tuple44[] memory) { require(false, stub_error); dummy; - return new Tuple42[](0); + return new Tuple44[](0); } /// Set the collection access method. @@ -614,13 +614,13 @@ } /// @dev anonymous struct -struct Tuple42 { +struct Tuple44 { CollectionPermissions field_0; bool field_1; } /// @dev anonymous struct -struct Tuple39 { +struct Tuple41 { bool field_0; uint256[] field_1; } @@ -648,18 +648,12 @@ } /// @dev anonymous struct -struct Tuple33 { +struct Tuple35 { CollectionLimits field_0; bool field_1; uint256 field_2; } -/// @dev anonymous struct -struct Tuple30 { - address field_0; - uint256 field_1; -} - /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 /// @dev the ERC-165 identifier for this interface is 0x5b5e139f @@ -735,7 +729,7 @@ return false; } - /// @notice Function to mint token. + /// @notice Function to mint a token. /// @param to The new owner /// @return uint256 The id of the newly minted token /// @dev EVM selector for this function is: 0x6a627842, @@ -747,7 +741,7 @@ return 0; } - // /// @notice Function to mint token. + // /// @notice Function to mint a token. // /// @dev `tokenId` should be obtained with `nextTokenId` method, // /// unlike standard, you can't specify it manually // /// @param to The new owner @@ -804,7 +798,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0xb74c26b7 +/// @dev the ERC-165 identifier for this interface is 0x0e48fdb4 contract ERC721UniqueExtensions is Dummy, ERC165 { /// @notice A descriptive name for a collection of NFTs in this contract /// @dev EVM selector for this function is: 0x06fdde03, @@ -961,6 +955,7 @@ dummy; return 0; } + // /// @notice Function to mint multiple tokens. // /// @dev `tokenIds` should be an array of consecutive numbers and first number // /// should be obtained with `nextTokenId` method @@ -991,6 +986,19 @@ // return false; // } + /// @notice Function to mint a token. + /// @param to The new owner crossAccountId + /// @param properties Properties of minted token + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0xb904db03, + /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) + function mintCross(EthCrossAccount memory to, Property[] memory properties) public returns (uint256) { + require(false, stub_error); + to; + properties; + dummy = 0; + return 0; + } } /// @dev anonymous struct --- a/pallets/refungible/CHANGELOG.md +++ b/pallets/refungible/CHANGELOG.md @@ -4,7 +4,7 @@ -## [0.2.10] - 2022-12-16 +## [0.2.11] - 2022-12-16 ### Added @@ -14,6 +14,12 @@ - Hide `setTokenPropertyPermission` function in `TokenProperties` interface. +## [0.2.10] - 2022-12-01 + +### Added + +- The functions `mintCross` to `ERC721UniqueExtensions` interface. + ## [0.2.9] - 2022-11-18 ### Added --- a/pallets/refungible/Cargo.toml +++ b/pallets/refungible/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-refungible" -version = "0.2.10" +version = "0.2.11" license = "GPLv3" edition = "2021" --- a/pallets/refungible/src/common.rs +++ b/pallets/refungible/src/common.rs @@ -165,9 +165,9 @@ fn map_create_data( data: up_data_structs::CreateItemData, to: &T::CrossAccountId, -) -> Result, DispatchError> { +) -> Result, DispatchError> { match data { - up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateItemData { + up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateItemData:: { users: { let mut out = BTreeMap::new(); out.insert(to.clone(), data.pieces); @@ -230,7 +230,7 @@ CreateItemExData::RefungibleMultipleOwners(CreateRefungibleExMultipleOwners { users, properties, - }) => vec![CreateItemData { users, properties }], + }) => vec![CreateItemData:: { users, properties }], CreateItemExData::RefungibleMultipleItems(r) => r .into_inner() .into_iter() @@ -239,7 +239,7 @@ user, pieces, properties, - }| CreateItemData { + }| CreateItemData:: { users: BTreeMap::from([(user, pieces)]) .try_into() .expect("limit >= 1"), --- a/pallets/refungible/src/erc.rs +++ b/pallets/refungible/src/erc.rs @@ -637,7 +637,7 @@ Ok(false) } - /// @notice Function to mint token. + /// @notice Function to mint a token. /// @param to The new owner /// @return uint256 The id of the newly minted token #[weight(>::create_item())] @@ -650,7 +650,7 @@ Ok(token_id) } - /// @notice Function to mint token. + /// @notice Function to mint a token. /// @dev `tokenId` should be obtained with `nextTokenId` method, /// unlike standard, you can't specify it manually /// @param to The new owner @@ -681,7 +681,7 @@ >::create_item( self, &caller, - CreateItemData:: { + CreateItemData:: { users, properties: CollectionPropertiesVec::default(), }, @@ -767,7 +767,7 @@ >::create_item( self, &caller, - CreateItemData:: { users, properties }, + CreateItemData:: { users, properties }, &budget, ) .map_err(dispatch_to_evm::)?; @@ -1048,7 +1048,7 @@ .collect::>() .try_into() .unwrap(); - let create_item_data = CreateItemData:: { + let create_item_data = CreateItemData:: { users, properties: CollectionPropertiesVec::default(), }; @@ -1108,7 +1108,7 @@ }) .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?; - let create_item_data = CreateItemData:: { + let create_item_data = CreateItemData:: { users: users.clone(), properties, }; @@ -1120,6 +1120,60 @@ Ok(true) } + /// @notice Function to mint a token. + /// @param to The new owner crossAccountId + /// @param properties Properties of minted token + /// @return uint256 The id of the newly minted token + #[weight(>::create_item())] + fn mint_cross( + &mut self, + caller: caller, + to: EthCrossAccount, + properties: Vec, + ) -> Result { + let token_id = >::get(self.id) + .checked_add(1) + .ok_or("item id overflow")?; + + let to = to.into_sub_cross_account::()?; + + let properties = properties + .into_iter() + .map(|PropertyStruct { key, value }| { + let key = >::from(key) + .try_into() + .map_err(|_| "key too large")?; + + let value = value.0.try_into().map_err(|_| "value too large")?; + + Ok(Property { key, value }) + }) + .collect::>>()? + .try_into() + .map_err(|_| Error::Revert(alloc::format!("too many properties")))?; + + let caller = T::CrossAccountId::from_eth(caller); + + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + let users = [(to, 1)] + .into_iter() + .collect::>() + .try_into() + .unwrap(); + >::create_item( + self, + &caller, + CreateItemData:: { users, properties }, + &budget, + ) + .map_err(dispatch_to_evm::)?; + + Ok(token_id.into()) + } + /// Returns EVM address for refungible token /// /// @param token ID of the token --- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -114,6 +114,7 @@ CreateCollectionData, CustomDataLimit, mapping::TokenAddressMapping, MAX_ITEMS_PER_BATCH, MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, TokenId, TrySetProperty, PropertiesPermissionMap, + CreateRefungibleExMultipleOwners, }; pub use pallet::*; @@ -124,13 +125,8 @@ pub mod erc_token; pub mod weights; -#[derive(Derivative, Clone)] -pub struct CreateItemData { - #[derivative(Debug(format_with = "bounded::map_debug"))] - pub users: BoundedBTreeMap>, - #[derivative(Debug(format_with = "bounded::vec_debug"))] - pub properties: CollectionPropertiesVec, -} +pub type CreateItemData = + CreateRefungibleExMultipleOwners<::CrossAccountId>; pub(crate) type SelfWeightOf = ::WeightInfo; /// Token data, stored independently from other data used to describe it @@ -913,7 +909,7 @@ pub fn create_multiple_items( collection: &RefungibleHandle, sender: &T::CrossAccountId, - data: Vec>, + data: Vec>, nesting_budget: &dyn Budget, ) -> DispatchResult { if !collection.is_owner_or_admin(sender) { @@ -1259,7 +1255,7 @@ pub fn create_item( collection: &RefungibleHandle, sender: &T::CrossAccountId, - data: CreateItemData, + data: CreateItemData, nesting_budget: &dyn Budget, ) -> DispatchResult { Self::create_multiple_items(collection, sender, vec![data], nesting_budget) --- a/pallets/refungible/src/stubs/UniqueRefungible.sol +++ b/pallets/refungible/src/stubs/UniqueRefungible.sol @@ -42,7 +42,7 @@ /// @param permissions Permissions for keys. /// @dev EVM selector for this function is: 0xbd92983a, /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(Tuple58[] memory permissions) public { + function setTokenPropertyPermissions(Tuple60[] memory permissions) public { require(false, stub_error); permissions; dummy = 0; @@ -51,10 +51,10 @@ /// @notice Get permissions for token properties. /// @dev EVM selector for this function is: 0xf23d7790, /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() public view returns (Tuple58[] memory) { + function tokenPropertyPermissions() public view returns (Tuple60[] memory) { require(false, stub_error); dummy; - return new Tuple58[](0); + return new Tuple60[](0); } // /// @notice Set token property value. @@ -144,13 +144,13 @@ } /// @dev anonymous struct -struct Tuple58 { +struct Tuple60 { string field_0; - Tuple56[] field_1; + Tuple58[] field_1; } /// @dev anonymous struct -struct Tuple56 { +struct Tuple58 { EthTokenPermissions field_0; bool field_1; } @@ -290,10 +290,10 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() public view returns (Tuple29 memory) { + function collectionSponsor() public view returns (EthCrossAccount memory) { require(false, stub_error); dummy; - return Tuple29(0x0000000000000000000000000000000000000000, 0); + return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); } /// Get current collection limits. @@ -311,10 +311,10 @@ /// Return `false` if a limit not set. /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() public view returns (Tuple32[] memory) { + function collectionLimits() public view returns (Tuple34[] memory) { require(false, stub_error); dummy; - return new Tuple32[](0); + return new Tuple34[](0); } /// Set limits for the collection. @@ -422,19 +422,19 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() public view returns (Tuple38 memory) { + function collectionNestingRestrictedCollectionIds() public view returns (Tuple40 memory) { require(false, stub_error); dummy; - return Tuple38(false, new uint256[](0)); + return Tuple40(false, new uint256[](0)); } /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() public view returns (Tuple41[] memory) { + function collectionNestingPermissions() public view returns (Tuple43[] memory) { require(false, stub_error); dummy; - return new Tuple41[](0); + return new Tuple43[](0); } /// Set the collection access method. @@ -614,13 +614,13 @@ } /// @dev anonymous struct -struct Tuple41 { +struct Tuple43 { CollectionPermissions field_0; bool field_1; } /// @dev anonymous struct -struct Tuple38 { +struct Tuple40 { bool field_0; uint256[] field_1; } @@ -648,18 +648,12 @@ } /// @dev anonymous struct -struct Tuple32 { +struct Tuple34 { CollectionLimits field_0; bool field_1; uint256 field_2; } -/// @dev anonymous struct -struct Tuple29 { - address field_0; - uint256 field_1; -} - /// @dev the ERC-165 identifier for this interface is 0x5b5e139f contract ERC721Metadata is Dummy, ERC165 { // /// @notice A descriptive name for a collection of NFTs in this contract @@ -733,7 +727,7 @@ return false; } - /// @notice Function to mint token. + /// @notice Function to mint a token. /// @param to The new owner /// @return uint256 The id of the newly minted token /// @dev EVM selector for this function is: 0x6a627842, @@ -745,7 +739,7 @@ return 0; } - // /// @notice Function to mint token. + // /// @notice Function to mint a token. // /// @dev `tokenId` should be obtained with `nextTokenId` method, // /// unlike standard, you can't specify it manually // /// @param to The new owner @@ -802,7 +796,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x12f7d6c1 +/// @dev the ERC-165 identifier for this interface is 0xabf30dc2 contract ERC721UniqueExtensions is Dummy, ERC165 { /// @notice A descriptive name for a collection of NFTs in this contract /// @dev EVM selector for this function is: 0x06fdde03, @@ -979,6 +973,20 @@ // return false; // } + /// @notice Function to mint a token. + /// @param to The new owner crossAccountId + /// @param properties Properties of minted token + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0xb904db03, + /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) + function mintCross(EthCrossAccount memory to, Property[] memory properties) public returns (uint256) { + require(false, stub_error); + to; + properties; + dummy = 0; + return 0; + } + /// Returns EVM address for refungible token /// /// @param token ID of the token --- a/tests/src/eth/abi/contractHelpers.json +++ b/tests/src/eth/abi/contractHelpers.json @@ -223,10 +223,10 @@ "outputs": [ { "components": [ - { "internalType": "address", "name": "field_0", "type": "address" }, - { "internalType": "uint256", "name": "field_1", "type": "uint256" } + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct Tuple0", + "internalType": "struct EthCrossAccount", "name": "", "type": "tuple" } --- a/tests/src/eth/abi/fungible.json +++ b/tests/src/eth/abi/fungible.json @@ -220,7 +220,7 @@ { "internalType": "bool", "name": "field_1", "type": "bool" }, { "internalType": "uint256", "name": "field_2", "type": "uint256" } ], - "internalType": "struct Tuple20[]", + "internalType": "struct Tuple23[]", "name": "", "type": "tuple[]" } @@ -241,7 +241,7 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple29[]", + "internalType": "struct Tuple32[]", "name": "", "type": "tuple[]" } @@ -262,7 +262,7 @@ "type": "uint256[]" } ], - "internalType": "struct Tuple26", + "internalType": "struct Tuple29", "name": "", "type": "tuple" } @@ -319,10 +319,10 @@ "outputs": [ { "components": [ - { "internalType": "address", "name": "field_0", "type": "address" }, - { "internalType": "uint256", "name": "field_1", "type": "uint256" } + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct Tuple8", + "internalType": "struct EthCrossAccount", "name": "", "type": "tuple" } @@ -408,7 +408,7 @@ { "internalType": "address", "name": "field_0", "type": "address" }, { "internalType": "uint256", "name": "field_1", "type": "uint256" } ], - "internalType": "struct Tuple8[]", + "internalType": "struct Tuple9[]", "name": "amounts", "type": "tuple[]" } @@ -419,6 +419,24 @@ "type": "function" }, { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct EthCrossAccount", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "mintCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [], "name": "name", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], --- a/tests/src/eth/abi/nonFungible.json +++ b/tests/src/eth/abi/nonFungible.json @@ -250,7 +250,7 @@ { "internalType": "bool", "name": "field_1", "type": "bool" }, { "internalType": "uint256", "name": "field_2", "type": "uint256" } ], - "internalType": "struct Tuple33[]", + "internalType": "struct Tuple35[]", "name": "", "type": "tuple[]" } @@ -271,7 +271,7 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple42[]", + "internalType": "struct Tuple44[]", "name": "", "type": "tuple[]" } @@ -292,7 +292,7 @@ "type": "uint256[]" } ], - "internalType": "struct Tuple39", + "internalType": "struct Tuple41", "name": "", "type": "tuple" } @@ -349,10 +349,10 @@ "outputs": [ { "components": [ - { "internalType": "address", "name": "field_0", "type": "address" }, - { "internalType": "uint256", "name": "field_1", "type": "uint256" } + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct Tuple30", + "internalType": "struct EthCrossAccount", "name": "", "type": "tuple" } @@ -478,6 +478,32 @@ }, { "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct EthCrossAccount", + "name": "to", + "type": "tuple" + }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "name": "mintCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "string", "name": "tokenUri", "type": "string" } ], @@ -736,12 +762,12 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple57[]", + "internalType": "struct Tuple59[]", "name": "field_1", "type": "tuple[]" } ], - "internalType": "struct Tuple59[]", + "internalType": "struct Tuple61[]", "name": "permissions", "type": "tuple[]" } @@ -802,12 +828,12 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple57[]", + "internalType": "struct Tuple59[]", "name": "field_1", "type": "tuple[]" } ], - "internalType": "struct Tuple59[]", + "internalType": "struct Tuple61[]", "name": "", "type": "tuple[]" } --- a/tests/src/eth/abi/reFungible.json +++ b/tests/src/eth/abi/reFungible.json @@ -232,7 +232,7 @@ { "internalType": "bool", "name": "field_1", "type": "bool" }, { "internalType": "uint256", "name": "field_2", "type": "uint256" } ], - "internalType": "struct Tuple32[]", + "internalType": "struct Tuple34[]", "name": "", "type": "tuple[]" } @@ -253,7 +253,7 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple41[]", + "internalType": "struct Tuple43[]", "name": "", "type": "tuple[]" } @@ -274,7 +274,7 @@ "type": "uint256[]" } ], - "internalType": "struct Tuple38", + "internalType": "struct Tuple40", "name": "", "type": "tuple" } @@ -331,10 +331,10 @@ "outputs": [ { "components": [ - { "internalType": "address", "name": "field_0", "type": "address" }, - { "internalType": "uint256", "name": "field_1", "type": "uint256" } + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct Tuple29", + "internalType": "struct EthCrossAccount", "name": "", "type": "tuple" } @@ -460,6 +460,32 @@ }, { "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct EthCrossAccount", + "name": "to", + "type": "tuple" + }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "name": "mintCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "string", "name": "tokenUri", "type": "string" } ], @@ -718,12 +744,12 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple56[]", + "internalType": "struct Tuple58[]", "name": "field_1", "type": "tuple[]" } ], - "internalType": "struct Tuple58[]", + "internalType": "struct Tuple60[]", "name": "permissions", "type": "tuple[]" } @@ -793,12 +819,12 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple56[]", + "internalType": "struct Tuple58[]", "name": "field_1", "type": "tuple[]" } ], - "internalType": "struct Tuple58[]", + "internalType": "struct Tuple60[]", "name": "", "type": "tuple[]" } --- a/tests/src/eth/api/ContractHelpers.sol +++ b/tests/src/eth/api/ContractHelpers.sol @@ -69,7 +69,7 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x766c4f37, /// or in textual repr: sponsor(address) - function sponsor(address contractAddress) external view returns (Tuple0 memory); + function sponsor(address contractAddress) external view returns (EthCrossAccount memory); /// Check tat contract has confirmed sponsor. /// @@ -171,8 +171,8 @@ function toggleAllowlist(address contractAddress, bool enabled) external; } -/// @dev anonymous struct -struct Tuple0 { - address field_0; - uint256 field_1; +/// @dev Cross account struct +struct EthCrossAccount { + address eth; + uint256 sub; } --- a/tests/src/eth/api/UniqueFungible.sol +++ b/tests/src/eth/api/UniqueFungible.sol @@ -102,7 +102,7 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (Tuple8 memory); + function collectionSponsor() external view returns (EthCrossAccount memory); /// Get current collection limits. /// @@ -119,7 +119,7 @@ /// Return `false` if a limit not set. /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() external view returns (Tuple19[] memory); + function collectionLimits() external view returns (Tuple21[] memory); /// Set limits for the collection. /// @dev Throws error if limit not found. @@ -191,12 +191,12 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() external view returns (Tuple24 memory); + function collectionNestingRestrictedCollectionIds() external view returns (Tuple26 memory); /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() external view returns (Tuple27[] memory); + function collectionNestingPermissions() external view returns (Tuple29[] memory); /// Set the collection access method. /// @param mode Access mode @@ -311,7 +311,7 @@ } /// @dev anonymous struct -struct Tuple27 { +struct Tuple29 { CollectionPermissions field_0; bool field_1; } @@ -322,7 +322,7 @@ } /// @dev anonymous struct -struct Tuple24 { +struct Tuple26 { bool field_0; uint256[] field_1; } @@ -350,7 +350,7 @@ } /// @dev anonymous struct -struct Tuple19 { +struct Tuple21 { CollectionLimits field_0; bool field_1; uint256 field_2; @@ -362,13 +362,17 @@ bytes value; } -/// @dev the ERC-165 identifier for this interface is 0x5b7038cf +/// @dev the ERC-165 identifier for this interface is 0x7dee5997 interface ERC20UniqueExtensions is Dummy, ERC165 { /// @notice A description for the collection. /// @dev EVM selector for this function is: 0x7284e416, /// or in textual repr: description() function description() external view returns (string memory); + /// @dev EVM selector for this function is: 0x269e6158, + /// or in textual repr: mintCross((address,uint256),uint256) + function mintCross(EthCrossAccount memory to, uint256 amount) external returns (bool); + /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) function approveCross(EthCrossAccount memory spender, uint256 amount) external returns (bool); @@ -395,7 +399,7 @@ /// @param amounts array of pairs of account address and amount /// @dev EVM selector for this function is: 0x1acf2d55, /// or in textual repr: mintBulk((address,uint256)[]) - function mintBulk(Tuple8[] memory amounts) external returns (bool); + function mintBulk(Tuple9[] memory amounts) external returns (bool); /// @dev EVM selector for this function is: 0x2ada85ff, /// or in textual repr: transferCross((address,uint256),uint256) @@ -411,7 +415,7 @@ } /// @dev anonymous struct -struct Tuple8 { +struct Tuple9 { address field_0; uint256 field_1; } --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -30,12 +30,12 @@ /// @param permissions Permissions for keys. /// @dev EVM selector for this function is: 0xbd92983a, /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(Tuple52[] memory permissions) external; + function setTokenPropertyPermissions(Tuple53[] memory permissions) external; /// @notice Get permissions for token properties. /// @dev EVM selector for this function is: 0xf23d7790, /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() external view returns (Tuple52[] memory); + function tokenPropertyPermissions() external view returns (Tuple53[] memory); // /// @notice Set token property value. // /// @dev Throws error if `msg.sender` has no permission to edit the property. @@ -97,13 +97,13 @@ } /// @dev anonymous struct -struct Tuple52 { +struct Tuple53 { string field_0; - Tuple50[] field_1; + Tuple51[] field_1; } /// @dev anonymous struct -struct Tuple50 { +struct Tuple51 { EthTokenPermissions field_0; bool field_1; } @@ -198,7 +198,7 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (Tuple27 memory); + function collectionSponsor() external view returns (EthCrossAccount memory); /// Get current collection limits. /// @@ -215,7 +215,7 @@ /// Return `false` if a limit not set. /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() external view returns (Tuple30[] memory); + function collectionLimits() external view returns (Tuple31[] memory); /// Set limits for the collection. /// @dev Throws error if limit not found. @@ -287,12 +287,12 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() external view returns (Tuple35 memory); + function collectionNestingRestrictedCollectionIds() external view returns (Tuple36 memory); /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() external view returns (Tuple38[] memory); + function collectionNestingPermissions() external view returns (Tuple39[] memory); /// Set the collection access method. /// @param mode Access mode @@ -407,7 +407,7 @@ } /// @dev anonymous struct -struct Tuple38 { +struct Tuple39 { CollectionPermissions field_0; bool field_1; } @@ -418,7 +418,7 @@ } /// @dev anonymous struct -struct Tuple35 { +struct Tuple36 { bool field_0; uint256[] field_1; } @@ -446,18 +446,12 @@ } /// @dev anonymous struct -struct Tuple30 { +struct Tuple31 { CollectionLimits field_0; bool field_1; uint256 field_2; } -/// @dev anonymous struct -struct Tuple27 { - address field_0; - uint256 field_1; -} - /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 /// @dev the ERC-165 identifier for this interface is 0x5b5e139f @@ -512,14 +506,14 @@ /// or in textual repr: mintingFinished() function mintingFinished() external view returns (bool); - /// @notice Function to mint token. + /// @notice Function to mint a token. /// @param to The new owner /// @return uint256 The id of the newly minted token /// @dev EVM selector for this function is: 0x6a627842, /// or in textual repr: mint(address) function mint(address to) external returns (uint256); - // /// @notice Function to mint token. + // /// @notice Function to mint a token. // /// @dev `tokenId` should be obtained with `nextTokenId` method, // /// unlike standard, you can't specify it manually // /// @param to The new owner @@ -553,7 +547,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0xb74c26b7 +/// @dev the ERC-165 identifier for this interface is 0x0e48fdb4 interface ERC721UniqueExtensions is Dummy, ERC165 { /// @notice A descriptive name for a collection of NFTs in this contract /// @dev EVM selector for this function is: 0x06fdde03, @@ -652,6 +646,7 @@ /// @dev EVM selector for this function is: 0x75794a3c, /// or in textual repr: nextTokenId() function nextTokenId() external view returns (uint256); + // /// @notice Function to mint multiple tokens. // /// @dev `tokenIds` should be an array of consecutive numbers and first number // /// should be obtained with `nextTokenId` method @@ -670,6 +665,13 @@ // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) // function mintBulkWithTokenURI(address to, Tuple13[] memory tokens) external returns (bool); + /// @notice Function to mint a token. + /// @param to The new owner crossAccountId + /// @param properties Properties of minted token + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0xb904db03, + /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) + function mintCross(EthCrossAccount memory to, Property[] memory properties) external returns (uint256); } /// @dev anonymous struct --- a/tests/src/eth/api/UniqueRefungible.sol +++ b/tests/src/eth/api/UniqueRefungible.sol @@ -30,12 +30,12 @@ /// @param permissions Permissions for keys. /// @dev EVM selector for this function is: 0xbd92983a, /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(Tuple51[] memory permissions) external; + function setTokenPropertyPermissions(Tuple52[] memory permissions) external; /// @notice Get permissions for token properties. /// @dev EVM selector for this function is: 0xf23d7790, /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() external view returns (Tuple51[] memory); + function tokenPropertyPermissions() external view returns (Tuple52[] memory); // /// @notice Set token property value. // /// @dev Throws error if `msg.sender` has no permission to edit the property. @@ -97,13 +97,13 @@ } /// @dev anonymous struct -struct Tuple51 { +struct Tuple52 { string field_0; - Tuple49[] field_1; + Tuple50[] field_1; } /// @dev anonymous struct -struct Tuple49 { +struct Tuple50 { EthTokenPermissions field_0; bool field_1; } @@ -198,7 +198,7 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (Tuple26 memory); + function collectionSponsor() external view returns (EthCrossAccount memory); /// Get current collection limits. /// @@ -215,7 +215,7 @@ /// Return `false` if a limit not set. /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() external view returns (Tuple29[] memory); + function collectionLimits() external view returns (Tuple30[] memory); /// Set limits for the collection. /// @dev Throws error if limit not found. @@ -287,12 +287,12 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() external view returns (Tuple34 memory); + function collectionNestingRestrictedCollectionIds() external view returns (Tuple35 memory); /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() external view returns (Tuple37[] memory); + function collectionNestingPermissions() external view returns (Tuple38[] memory); /// Set the collection access method. /// @param mode Access mode @@ -407,7 +407,7 @@ } /// @dev anonymous struct -struct Tuple37 { +struct Tuple38 { CollectionPermissions field_0; bool field_1; } @@ -418,7 +418,7 @@ } /// @dev anonymous struct -struct Tuple34 { +struct Tuple35 { bool field_0; uint256[] field_1; } @@ -446,18 +446,12 @@ } /// @dev anonymous struct -struct Tuple29 { +struct Tuple30 { CollectionLimits field_0; bool field_1; uint256 field_2; } -/// @dev anonymous struct -struct Tuple26 { - address field_0; - uint256 field_1; -} - /// @dev the ERC-165 identifier for this interface is 0x5b5e139f interface ERC721Metadata is Dummy, ERC165 { // /// @notice A descriptive name for a collection of NFTs in this contract @@ -510,14 +504,14 @@ /// or in textual repr: mintingFinished() function mintingFinished() external view returns (bool); - /// @notice Function to mint token. + /// @notice Function to mint a token. /// @param to The new owner /// @return uint256 The id of the newly minted token /// @dev EVM selector for this function is: 0x6a627842, /// or in textual repr: mint(address) function mint(address to) external returns (uint256); - // /// @notice Function to mint token. + // /// @notice Function to mint a token. // /// @dev `tokenId` should be obtained with `nextTokenId` method, // /// unlike standard, you can't specify it manually // /// @param to The new owner @@ -551,7 +545,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x12f7d6c1 +/// @dev the ERC-165 identifier for this interface is 0xabf30dc2 interface ERC721UniqueExtensions is Dummy, ERC165 { /// @notice A descriptive name for a collection of NFTs in this contract /// @dev EVM selector for this function is: 0x06fdde03, @@ -663,6 +657,14 @@ // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) // function mintBulkWithTokenURI(address to, Tuple12[] memory tokens) external returns (bool); + /// @notice Function to mint a token. + /// @param to The new owner crossAccountId + /// @param properties Properties of minted token + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0xb904db03, + /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) + function mintCross(EthCrossAccount memory to, Property[] memory properties) external returns (uint256); + /// Returns EVM address for refungible token /// /// @param token ID of the token --- a/tests/src/eth/base.test.ts +++ b/tests/src/eth/base.test.ts @@ -116,7 +116,7 @@ await checkInterface(helper, '0x780e9d63', true, true); }); - itEth('ERC721UniqueExtensions support', async ({helper}) => { + itEth.skip('ERC721UniqueExtensions support', async ({helper}) => { await checkInterface(helper, '0xb74c26b7', true, true); }); --- a/tests/src/eth/collectionSponsoring.test.ts +++ b/tests/src/eth/collectionSponsoring.test.ts @@ -106,7 +106,7 @@ await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner}); - expect(sponsorTuple.field_0).to.be.eq('0x0000000000000000000000000000000000000000'); + expect(sponsorTuple.eth).to.be.eq('0x0000000000000000000000000000000000000000'); })); [ --- a/tests/src/eth/fungible.test.ts +++ b/tests/src/eth/fungible.test.ts @@ -57,7 +57,7 @@ before(async function() { await usingEthPlaygrounds(async (helper, privateKey) => { donor = await privateKey({filename: __filename}); - [alice, owner] = await helper.arrange.createAccounts([20n, 20n], donor); + [alice, owner] = await helper.arrange.createAccounts([30n, 20n], donor); }); }); @@ -78,6 +78,25 @@ expect(event.returnValues.to).to.equal(receiver); expect(event.returnValues.value).to.equal('100'); }); + + + itEth('Can perform mintCross()', async ({helper}) => { + const receiverCross = helper.ethCrossAccount.fromKeyringPair(owner); + const ethOwner = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.ft.mintCollection(alice); + await collection.addAdmin(alice, {Ethereum: ethOwner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner); + + const result = await contract.methods.mintCross(receiverCross, 100).send(); + + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(helper.address.substrateToEth(owner.address)); + expect(event.returnValues.value).to.equal('100'); + }); itEth('Can perform mintBulk()', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); --- a/tests/src/eth/nesting/nest.test.ts +++ b/tests/src/eth/nesting/nest.test.ts @@ -62,9 +62,9 @@ expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, []]); await contract.methods.setCollectionNesting(true, [unnsetedCollectionAddress]).send({from: owner}); expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, [unnestedCollsectionId.toString()]]); - expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['0', false], ['1', true]]); + expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', true]]); await contract.methods.setCollectionNesting(false).send({from: owner}); - expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['0', false], ['1', false]]); + expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', false]]); }); itEth('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => { --- a/tests/src/eth/nonFungible.test.ts +++ b/tests/src/eth/nonFungible.test.ts @@ -17,6 +17,7 @@ import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util'; import {IKeyringPair} from '@polkadot/types/types'; import {Contract} from 'web3-eth-contract'; +import {ITokenPropertyPermission} from '../util/playgrounds/types'; describe('NFT: Information getting', () => { @@ -173,7 +174,58 @@ // const tokenUri = await contract.methods.tokenURI(nextTokenId).call(); // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`); }); + + itEth('Can perform mintCross()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob); + const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; }); + const permissions: ITokenPropertyPermission[] = properties + .map(p => { + return { + key: p.key, permission: { + tokenOwner: true, + collectionAdmin: true, + mutable: true, + }, + }; + }); + + + const collection = await helper.nft.mintCollection(minter, { + tokenPrefix: 'ethp', + tokenPropertyPermissions: permissions, + }); + await collection.addAdmin(minter, {Ethereum: caller}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller, true); + let expectedTokenId = await contract.methods.nextTokenId().call(); + let result = await contract.methods.mintCross(receiverCross, []).send(); + let tokenId = result.events.Transfer.returnValues.tokenId; + expect(tokenId).to.be.equal(expectedTokenId); + + let event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address)); + expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); + + expectedTokenId = await contract.methods.nextTokenId().call(); + result = await contract.methods.mintCross(receiverCross, properties).send(); + event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address)); + expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); + + tokenId = result.events.Transfer.returnValues.tokenId; + + expect(tokenId).to.be.equal(expectedTokenId); + expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties + .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); })); + }); + //TODO: CORE-302 add eth methods itEth.skip('Can perform mintBulk()', async ({helper}) => { const caller = await helper.eth.createAccountWithBalance(donor); --- a/tests/src/eth/reFungible.test.ts +++ b/tests/src/eth/reFungible.test.ts @@ -17,6 +17,7 @@ import {Pallets, requirePalletsOrSkip} from '../util'; import {expect, itEth, usingEthPlaygrounds} from './util'; import {IKeyringPair} from '@polkadot/types/types'; +import {ITokenPropertyPermission} from '../util/playgrounds/types'; describe('Refungible: Information getting', () => { let donor: IKeyringPair; @@ -135,6 +136,50 @@ expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']); expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); }); + + itEth('Can perform mintCross()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob); + const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; }); + const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {tokenOwner: true, + collectionAdmin: true, + mutable: true}}; }); + + + const collection = await helper.rft.mintCollection(minter, { + tokenPrefix: 'ethp', + tokenPropertyPermissions: permissions, + }); + await collection.addAdmin(minter, {Ethereum: caller}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller, true); + let expectedTokenId = await contract.methods.nextTokenId().call(); + let result = await contract.methods.mintCross(receiverCross, []).send(); + let tokenId = result.events.Transfer.returnValues.tokenId; + expect(tokenId).to.be.equal(expectedTokenId); + + let event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address)); + expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); + + expectedTokenId = await contract.methods.nextTokenId().call(); + result = await contract.methods.mintCross(receiverCross, properties).send(); + event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address)); + expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); + + tokenId = result.events.Transfer.returnValues.tokenId; + + expect(tokenId).to.be.equal(expectedTokenId); + + expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties + .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); })); + }); itEth.skip('Can perform mintBulk()', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); --- a/tests/src/interfaces/augment-api-consts.ts +++ b/tests/src/interfaces/augment-api-consts.ts @@ -83,7 +83,7 @@ dayRelayBlocks: u32 & AugmentedConst; defaultMinGasPrice: u64 & AugmentedConst; defaultWeightToFeeCoefficient: u32 & AugmentedConst; - maxOverridedAllowedLocations: u32 & AugmentedConst; + maxXcmAllowedLocations: u32 & AugmentedConst; /** * Generic const **/ -- gitstuff