--- a/pallets/common/src/eth.rs +++ b/pallets/common/src/eth.rs @@ -631,3 +631,12 @@ } } } + +/// Token minting parameters +#[derive(AbiCoder, Default, Debug)] +pub struct MintTokenData { + /// Minted token owner + pub owner: CrossAddress, + /// Minted token properties + pub properties: Vec, +} --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -981,13 +981,41 @@ Ok(true) } + /// @notice Function to mint a token. + /// @param data Array of pairs of token owner and token's properties for minted token + #[weight(>::create_multiple_items(data.len() as u32) + >::set_token_properties(data.len() as u32))] + fn mint_bulk_cross(&mut self, caller: Caller, data: Vec) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + let mut create_nft_data = Vec::with_capacity(data.len()); + for eth::MintTokenData { owner, properties } in data { + let owner = owner.into_sub_cross_account::()?; + create_nft_data.push(CreateItemData:: { + properties: properties + .into_iter() + .map(|property| property.try_into()) + .collect::>>()? + .try_into() + .map_err(|_| "too many properties")?, + owner, + }); + } + + >::create_multiple_items(self, &caller, create_nft_data, &budget) + .map_err(dispatch_to_evm::)?; + Ok(true) + } + /// @notice Function to mint multiple tokens with the given tokenUris. /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive /// numbers and first number should be obtained with `nextTokenId` method /// @param to The new owner /// @param tokens array of pairs of token ID and token URI for minted tokens #[solidity(hide, rename_selector = "mintBulkWithTokenURI")] - #[weight(>::create_multiple_items(tokens.len() as u32) + >::set_token_properties(tokens.len() as u32))] + #[weight(>::create_multiple_items(tokens.len() as u32) + >::set_token_properties(tokens.len() as u32))] fn mint_bulk_with_token_uri( &mut self, caller: Caller, --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -800,7 +800,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x307b061a +/// @dev the ERC-165 identifier for this interface is 0x9b397d16 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, @@ -997,6 +997,17 @@ // return false; // } + /// @notice Function to mint a token. + /// @param data Array of pairs of token owner and token's properties for minted token + /// @dev EVM selector for this function is: 0xab427b0c, + /// or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[]) + function mintBulkCross(MintTokenData[] memory data) public returns (bool) { + require(false, stub_error); + data; + dummy = 0; + return false; + } + // /// @notice Function to mint multiple tokens with the given tokenUris. // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive // /// numbers and first number should be obtained with `nextTokenId` method @@ -1044,6 +1055,11 @@ string uri; } +struct MintTokenData { + CrossAddress owner; + Property[] properties; +} + /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 /// @dev the ERC-165 identifier for this interface is 0x780e9d63 --- a/pallets/refungible/src/erc.rs +++ b/pallets/refungible/src/erc.rs @@ -1021,6 +1021,43 @@ Ok(true) } + /// @notice Function to mint a token. + /// @param tokenProperties Properties of minted token + #[weight(>::create_multiple_items(token_properties.len() as u32) + >::set_token_properties(token_properties.len() as u32))] + fn mint_bulk_cross( + &mut self, + caller: Caller, + token_properties: Vec, + ) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + let mut create_rft_data = Vec::with_capacity(token_properties.len()); + for eth::MintTokenData { owner, properties } in token_properties { + let owner = owner.into_sub_cross_account::()?; + let users: BoundedBTreeMap<_, _, _> = [(owner, 1)] + .into_iter() + .collect::>() + .try_into() + .map_err(|_| "too many users")?; + create_rft_data.push(CreateItemData:: { + properties: properties + .into_iter() + .map(|property| property.try_into()) + .collect::>>()? + .try_into() + .map_err(|_| "too many properties")?, + users, + }); + } + + >::create_multiple_items(self, &caller, create_rft_data, &budget) + .map_err(dispatch_to_evm::)?; + Ok(true) + } + /// @notice Function to mint multiple tokens with the given tokenUris. /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive /// numbers and first number should be obtained with `nextTokenId` method --- a/pallets/refungible/src/stubs/UniqueRefungible.sol +++ b/pallets/refungible/src/stubs/UniqueRefungible.sol @@ -800,7 +800,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x95c0f66c +/// @dev the ERC-165 identifier for this interface is 0x3e828d60 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, @@ -986,6 +986,17 @@ // return false; // } + /// @notice Function to mint a token. + /// @param tokenProperties Properties of minted token + /// @dev EVM selector for this function is: 0xab427b0c, + /// or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[]) + function mintBulkCross(MintTokenData[] memory tokenProperties) public returns (bool) { + require(false, stub_error); + tokenProperties; + dummy = 0; + return false; + } + // /// @notice Function to mint multiple tokens with the given tokenUris. // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive // /// numbers and first number should be obtained with `nextTokenId` method @@ -1045,6 +1056,11 @@ string uri; } +struct MintTokenData { + CrossAddress owner; + Property[] properties; +} + /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 /// @dev the ERC-165 identifier for this interface is 0x780e9d63 --- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol +++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol @@ -25,12 +25,12 @@ } /// @title Contract, which allows users to operate with collections -/// @dev the ERC-165 identifier for this interface is 0x4135fff1 +/// @dev the ERC-165 identifier for this interface is 0x94e5af0d contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents { /// Create a collection /// @return address Address of the newly created collection - /// @dev EVM selector for this function is: 0xa765ee5b, - /// or in textual repr: createCollection(((address,uint256),string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],uint8)) + /// @dev EVM selector for this function is: 0x72b5bea7, + /// or in textual repr: createCollection((string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],(address,uint256),uint8)) function createCollection(CreateCollectionData memory data) public payable returns (address) { require(false, stub_error); data; @@ -170,8 +170,6 @@ /// Collection properties struct CreateCollectionData { - /// Collection sponsor - CrossAddress pending_sponsor; /// Collection name string name; /// Collection description @@ -192,11 +190,12 @@ CollectionNestingAndPermission nesting_settings; /// Collection limits CollectionLimitValue[] limits; + /// Collection sponsor + CrossAddress pending_sponsor; /// Extra collection flags CollectionFlags flags; } -/// Cross account struct type CollectionFlags is uint8; library CollectionFlagsLib { @@ -207,13 +206,19 @@ /// External collections can't be managed using `unique` api CollectionFlags constant externalField = CollectionFlags.wrap(1); - /// Reserved bits + /// Reserved flags function reservedField(uint8 value) public pure returns (CollectionFlags) { require(value < 1 << 5, "out of bound value"); return CollectionFlags.wrap(value << 1); } } +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + /// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. struct CollectionLimitValue { CollectionLimitField field; @@ -250,12 +255,6 @@ bool collection_admin; /// If set - only tokens from specified collections can be nested. address[] restricted; -} - -/// Cross account struct -struct CrossAddress { - address eth; - uint256 sub; } /// Ethereum representation of Token Property Permissions. @@ -292,10 +291,10 @@ /// Type of tokens in collection enum CollectionMode { - /// Fungible - Fungible, /// Nonfungible Nonfungible, + /// Fungible + Fungible, /// Refungible Refungible } --- a/runtime/common/ethereum/sponsoring/refungible.rs +++ b/runtime/common/ethereum/sponsoring/refungible.rs @@ -242,6 +242,7 @@ BurnFrom { .. } | BurnFromCross { .. } | MintBulk { .. } + | MintBulkCross { .. } | MintBulkWithTokenUri { .. } => None, MintCross { .. } => withdraw_create_item::( --- a/tests/src/eth/abi/nonFungible.json +++ b/tests/src/eth/abi/nonFungible.json @@ -469,6 +469,39 @@ "inputs": [ { "components": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "internalType": "struct MintTokenData[]", + "name": "data", + "type": "tuple[]" + } + ], + "name": "mintBulkCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], --- a/tests/src/eth/abi/reFungible.json +++ b/tests/src/eth/abi/reFungible.json @@ -451,6 +451,39 @@ "inputs": [ { "components": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "internalType": "struct MintTokenData[]", + "name": "tokenProperties", + "type": "tuple[]" + } + ], + "name": "mintBulkCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], --- a/tests/src/eth/api/CollectionHelpers.sol +++ b/tests/src/eth/api/CollectionHelpers.sol @@ -20,12 +20,12 @@ } /// @title Contract, which allows users to operate with collections -/// @dev the ERC-165 identifier for this interface is 0x4135fff1 +/// @dev the ERC-165 identifier for this interface is 0x94e5af0d interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents { /// Create a collection /// @return address Address of the newly created collection - /// @dev EVM selector for this function is: 0xa765ee5b, - /// or in textual repr: createCollection(((address,uint256),string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],uint8)) + /// @dev EVM selector for this function is: 0x72b5bea7, + /// or in textual repr: createCollection((string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],(address,uint256),uint8)) function createCollection(CreateCollectionData memory data) external payable returns (address); /// Create an NFT collection @@ -103,8 +103,6 @@ /// Collection properties struct CreateCollectionData { - /// Collection sponsor - CrossAddress pending_sponsor; /// Collection name string name; /// Collection description @@ -125,11 +123,12 @@ CollectionNestingAndPermission nesting_settings; /// Collection limits CollectionLimitValue[] limits; + /// Collection sponsor + CrossAddress pending_sponsor; /// Extra collection flags CollectionFlags flags; } -/// Cross account struct type CollectionFlags is uint8; library CollectionFlagsLib { @@ -140,13 +139,19 @@ /// External collections can't be managed using `unique` api CollectionFlags constant externalField = CollectionFlags.wrap(1); - /// Reserved bits + /// Reserved flags function reservedField(uint8 value) public pure returns (CollectionFlags) { require(value < 1 << 5, "out of bound value"); return CollectionFlags.wrap(value << 1); } } +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + /// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. struct CollectionLimitValue { CollectionLimitField field; @@ -183,12 +188,6 @@ bool collection_admin; /// If set - only tokens from specified collections can be nested. address[] restricted; -} - -/// Cross account struct -struct CrossAddress { - address eth; - uint256 sub; } /// Ethereum representation of Token Property Permissions. @@ -225,10 +224,10 @@ /// Type of tokens in collection enum CollectionMode { - /// Fungible - Fungible, /// Nonfungible Nonfungible, + /// Fungible + Fungible, /// Refungible Refungible } --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -551,7 +551,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x307b061a +/// @dev the ERC-165 identifier for this interface is 0x9b397d16 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, @@ -674,6 +674,12 @@ // /// or in textual repr: mintBulk(address,uint256[]) // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool); + /// @notice Function to mint a token. + /// @param data Array of pairs of token owner and token's properties for minted token + /// @dev EVM selector for this function is: 0xab427b0c, + /// or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[]) + function mintBulkCross(MintTokenData[] memory data) external returns (bool); + // /// @notice Function to mint multiple tokens with the given tokenUris. // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive // /// numbers and first number should be obtained with `nextTokenId` method @@ -705,6 +711,11 @@ string uri; } +struct MintTokenData { + CrossAddress owner; + Property[] properties; +} + /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 /// @dev the ERC-165 identifier for this interface is 0x780e9d63 --- a/tests/src/eth/api/UniqueRefungible.sol +++ b/tests/src/eth/api/UniqueRefungible.sol @@ -551,7 +551,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x95c0f66c +/// @dev the ERC-165 identifier for this interface is 0x3e828d60 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, @@ -668,6 +668,12 @@ // /// or in textual repr: mintBulk(address,uint256[]) // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool); + /// @notice Function to mint a token. + /// @param tokenProperties Properties of minted token + /// @dev EVM selector for this function is: 0xab427b0c, + /// or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[]) + function mintBulkCross(MintTokenData[] memory tokenProperties) external returns (bool); + // /// @notice Function to mint multiple tokens with the given tokenUris. // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive // /// numbers and first number should be obtained with `nextTokenId` method @@ -706,6 +712,11 @@ string uri; } +struct MintTokenData { + CrossAddress owner; + Property[] properties; +} + /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 /// @dev the ERC-165 identifier for this interface is 0x780e9d63