--- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -37,6 +37,7 @@ Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf, eth::{ EthCrossAccount, convert_cross_account_to_uint256, CollectionPermissions as EvmPermissions, + CollectionLimits as EvmCollectionLimits, }, weights::WeightInfo, }; @@ -304,6 +305,99 @@ Ok(result) } + /// Get current collection limits. + /// + /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: + /// "accountTokenOwnershipLimit", + /// "sponsoredDataSize", + /// "sponsoredDataRateLimit", + /// "tokenLimit", + /// "sponsorTransferTimeout", + /// "sponsorApproveTimeout" + /// "ownerCanTransfer", + /// "ownerCanDestroy", + /// "transfersEnabled" + /// Return `false` if a limit not set. + fn collection_limits(&self) -> Result> { + let convert_value_limit = |limit: EvmCollectionLimits, + value: Option| + -> (EvmCollectionLimits, bool, uint256) { + value + .map(|v| (limit, true, v.into())) + .unwrap_or((limit, false, Default::default())) + }; + + let convert_bool_limit = |limit: EvmCollectionLimits, + value: Option| + -> (EvmCollectionLimits, bool, uint256) { + value + .map(|v| { + ( + limit, + true, + if v { + uint256::from(1) + } else { + Default::default() + }, + ) + }) + .unwrap_or((limit, false, Default::default())) + }; + + let limits = &self.collection.limits; + + Ok(vec![ + convert_value_limit( + EvmCollectionLimits::AccountTokenOwnership, + limits.account_token_ownership_limit, + ), + convert_value_limit( + EvmCollectionLimits::SponsoredDataSize, + limits.sponsored_data_size, + ), + limits + .sponsored_data_rate_limit + .and_then(|limit| { + if let SponsoringRateLimit::Blocks(blocks) = limit { + Some(( + EvmCollectionLimits::SponsoredDataRateLimit, + true, + blocks.into(), + )) + } else { + None + } + }) + .unwrap_or(( + EvmCollectionLimits::SponsoredDataRateLimit, + false, + Default::default(), + )), + convert_value_limit(EvmCollectionLimits::TokenLimit, limits.token_limit), + convert_value_limit( + EvmCollectionLimits::SponsorTransferTimeout, + limits.sponsor_transfer_timeout, + ), + convert_value_limit( + EvmCollectionLimits::SponsorApproveTimeout, + limits.sponsor_approve_timeout, + ), + convert_bool_limit( + EvmCollectionLimits::OwnerCanTransfer, + limits.owner_can_transfer, + ), + convert_bool_limit( + EvmCollectionLimits::OwnerCanDestroy, + limits.owner_can_destroy, + ), + convert_bool_limit( + EvmCollectionLimits::TransferEnabled, + limits.transfers_enabled, + ), + ]) + } + /// Set limits for the collection. /// @dev Throws error if limit not found. /// @param limit Name of the limit. Valid names: @@ -316,11 +410,22 @@ /// "ownerCanTransfer", /// "ownerCanDestroy", /// "transfersEnabled" + /// @param status enable\disable limit. Works only with `true`. /// @param value Value of the limit. #[solidity(rename_selector = "setCollectionLimit")] - fn set_int_limit(&mut self, caller: caller, limit: string, value: uint256) -> Result { + fn set_collection_limit( + &mut self, + caller: caller, + limit: EvmCollectionLimits, + status: bool, + value: uint256, + ) -> Result { self.consume_store_reads_and_writes(1, 1)?; + if !status { + return Err(Error::Revert("user can't disable limits".into())); + } + let value = value .try_into() .map_err(|_| Error::Revert(format!("can't convert value to u32 \"{}\"", value)))?; @@ -338,35 +443,35 @@ let mut limits = self.limits.clone(); - match limit.as_str() { - "accountTokenOwnershipLimit" => { + match limit { + EvmCollectionLimits::AccountTokenOwnership => { limits.account_token_ownership_limit = Some(value); } - "sponsoredDataSize" => { + EvmCollectionLimits::SponsoredDataSize => { limits.sponsored_data_size = Some(value); } - "sponsoredDataRateLimit" => { + EvmCollectionLimits::SponsoredDataRateLimit => { limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value)); } - "tokenLimit" => { + EvmCollectionLimits::TokenLimit => { limits.token_limit = Some(value); } - "sponsorTransferTimeout" => { + EvmCollectionLimits::SponsorTransferTimeout => { limits.sponsor_transfer_timeout = Some(value); } - "sponsorApproveTimeout" => { + EvmCollectionLimits::SponsorApproveTimeout => { limits.sponsor_approve_timeout = Some(value); } - "ownerCanTransfer" => { + EvmCollectionLimits::OwnerCanTransfer => { limits.owner_can_transfer = Some(convert_value_to_bool()?); } - "ownerCanDestroy" => { + EvmCollectionLimits::OwnerCanDestroy => { limits.owner_can_destroy = Some(convert_value_to_bool()?); } - "transfersEnabled" => { + EvmCollectionLimits::TransferEnabled => { limits.transfers_enabled = Some(convert_value_to_bool()?); } - _ => return Err(Error::Revert(format!("unknown limit \"{}\"", limit))), + _ => return Err(Error::Revert(format!("unknown limit \"{:?}\"", limit))), } let caller = T::CrossAccountId::from_eth(caller); --- a/pallets/common/src/eth.rs +++ b/pallets/common/src/eth.rs @@ -155,6 +155,31 @@ } } } + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. +#[derive(Debug, Default, Clone, Copy, AbiCoder)] +#[repr(u8)] +pub enum CollectionLimits { + /// 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, +} #[derive(Default, Debug, Clone, Copy, AbiCoder)] #[repr(u8)] pub enum CollectionPermissions { --- a/pallets/fungible/src/stubs/UniqueFungible.sol +++ b/pallets/fungible/src/stubs/UniqueFungible.sol @@ -18,7 +18,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xb5e1747f +/// @dev the ERC-165 identifier for this interface is 0x81172a75 contract Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -158,6 +158,27 @@ return Tuple8(0x0000000000000000000000000000000000000000, 0); } + /// Get current collection limits. + /// + /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: + /// "accountTokenOwnershipLimit", + /// "sponsoredDataSize", + /// "sponsoredDataRateLimit", + /// "tokenLimit", + /// "sponsorTransferTimeout", + /// "sponsorApproveTimeout" + /// "ownerCanTransfer", + /// "ownerCanDestroy", + /// "transfersEnabled" + /// 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) { + require(false, stub_error); + dummy; + return new Tuple20[](0); + } + /// Set limits for the collection. /// @dev Throws error if limit not found. /// @param limit Name of the limit. Valid names: @@ -170,12 +191,18 @@ /// "ownerCanTransfer", /// "ownerCanDestroy", /// "transfersEnabled" + /// @param status enable\disable limit. Works only with `true`. /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x4ad890a8, - /// or in textual repr: setCollectionLimit(string,uint256) - function setCollectionLimit(string memory limit, uint256 value) public { + /// @dev EVM selector for this function is: 0x88150bd0, + /// or in textual repr: setCollectionLimit(uint8,bool,uint256) + function setCollectionLimit( + CollectionLimits limit, + bool status, + uint256 value + ) public { require(false, stub_error); limit; + status; value; dummy = 0; } @@ -257,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 (Tuple21 memory) { + function collectionNestingRestrictedCollectionIds() public view returns (Tuple26 memory) { require(false, stub_error); dummy; - return Tuple21(false, new uint256[](0)); + return Tuple26(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 (Tuple24[] memory) { + function collectionNestingPermissions() public view returns (Tuple29[] memory) { require(false, stub_error); dummy; - return new Tuple24[](0); + return new Tuple29[](0); } /// Set the collection access method. @@ -449,17 +476,46 @@ } /// @dev anonymous struct -struct Tuple24 { +struct Tuple29 { CollectionPermissions field_0; bool field_1; } /// @dev anonymous struct -struct Tuple21 { +struct Tuple26 { bool field_0; uint256[] field_1; } +/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. +enum CollectionLimits { + /// @dev How many tokens can a user have on one account. + AccountTokenOwnership, + /// @dev How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// @dev How many tokens can be mined into this collection. + TokenLimit, + /// @dev Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// @dev Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// @dev Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// @dev Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// @dev anonymous struct +struct Tuple20 { + CollectionLimits field_0; + bool field_1; + uint256 field_2; +} + /// @dev Property struct struct Property { string key; --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -42,18 +42,19 @@ /// @param permissions Permissions for keys. /// @dev EVM selector for this function is: 0xbd92983a, /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(Tuple48[] memory permissions) public { + function setTokenPropertyPermissions(Tuple59[] memory permissions) public { require(false, stub_error); permissions; dummy = 0; } + /// @notice Get permissions for token properties. /// @dev EVM selector for this function is: 0xf23d7790, /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() public view returns (Tuple48[] memory) { + function tokenPropertyPermissions() public view returns (Tuple59[] memory) { require(false, stub_error); dummy; - return new Tuple48[](0); + return new Tuple59[](0); } // /// @notice Set token property value. @@ -132,26 +133,30 @@ bytes value; } +/// @dev Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. enum EthTokenPermissions { + /// @dev Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] Mutable, + /// @dev Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] TokenOwner, + /// @dev Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] CollectionAdmin } /// @dev anonymous struct -struct Tuple48 { +struct Tuple59 { string field_0; - Tuple46[] field_1; + Tuple57[] field_1; } /// @dev anonymous struct -struct Tuple46 { +struct Tuple57 { EthTokenPermissions field_0; bool field_1; } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xb5e1747f +/// @dev the ERC-165 identifier for this interface is 0x81172a75 contract Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -291,6 +296,27 @@ return Tuple30(0x0000000000000000000000000000000000000000, 0); } + /// Get current collection limits. + /// + /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: + /// "accountTokenOwnershipLimit", + /// "sponsoredDataSize", + /// "sponsoredDataRateLimit", + /// "tokenLimit", + /// "sponsorTransferTimeout", + /// "sponsorApproveTimeout" + /// "ownerCanTransfer", + /// "ownerCanDestroy", + /// "transfersEnabled" + /// 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) { + require(false, stub_error); + dummy; + return new Tuple33[](0); + } + /// Set limits for the collection. /// @dev Throws error if limit not found. /// @param limit Name of the limit. Valid names: @@ -303,12 +329,18 @@ /// "ownerCanTransfer", /// "ownerCanDestroy", /// "transfersEnabled" + /// @param status enable\disable limit. Works only with `true`. /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x4ad890a8, - /// or in textual repr: setCollectionLimit(string,uint256) - function setCollectionLimit(string memory limit, uint256 value) public { + /// @dev EVM selector for this function is: 0x88150bd0, + /// or in textual repr: setCollectionLimit(uint8,bool,uint256) + function setCollectionLimit( + CollectionLimits limit, + bool status, + uint256 value + ) public { require(false, stub_error); limit; + status; value; dummy = 0; } @@ -390,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 (Tuple34 memory) { + function collectionNestingRestrictedCollectionIds() public view returns (Tuple39 memory) { require(false, stub_error); dummy; - return Tuple34(false, new uint256[](0)); + return Tuple39(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 (Tuple37[] memory) { + function collectionNestingPermissions() public view returns (Tuple42[] memory) { require(false, stub_error); dummy; - return new Tuple37[](0); + return new Tuple42[](0); } /// Set the collection access method. @@ -582,17 +614,46 @@ } /// @dev anonymous struct -struct Tuple37 { +struct Tuple42 { CollectionPermissions field_0; bool field_1; } /// @dev anonymous struct -struct Tuple34 { +struct Tuple39 { bool field_0; uint256[] field_1; } +/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. +enum CollectionLimits { + /// @dev How many tokens can a user have on one account. + AccountTokenOwnership, + /// @dev How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// @dev How many tokens can be mined into this collection. + TokenLimit, + /// @dev Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// @dev Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// @dev Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// @dev Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// @dev anonymous struct +struct Tuple33 { + CollectionLimits field_0; + bool field_1; + uint256 field_2; +} + /// @dev anonymous struct struct Tuple30 { address field_0; --- 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(Tuple53[] memory permissions) public { + function setTokenPropertyPermissions(Tuple58[] 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 (Tuple53[] memory) { + function tokenPropertyPermissions() public view returns (Tuple58[] memory) { require(false, stub_error); dummy; - return new Tuple53[](0); + return new Tuple58[](0); } // /// @notice Set token property value. @@ -133,26 +133,30 @@ bytes value; } +/// @dev Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. enum EthTokenPermissions { + /// @dev Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] Mutable, + /// @dev Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] TokenOwner, + /// @dev Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] CollectionAdmin } /// @dev anonymous struct -struct Tuple53 { +struct Tuple58 { string field_0; - Tuple51[] field_1; + Tuple56[] field_1; } /// @dev anonymous struct -struct Tuple51 { +struct Tuple56 { EthTokenPermissions field_0; bool field_1; } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xb5e1747f +/// @dev the ERC-165 identifier for this interface is 0x81172a75 contract Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -292,6 +296,27 @@ return Tuple29(0x0000000000000000000000000000000000000000, 0); } + /// Get current collection limits. + /// + /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: + /// "accountTokenOwnershipLimit", + /// "sponsoredDataSize", + /// "sponsoredDataRateLimit", + /// "tokenLimit", + /// "sponsorTransferTimeout", + /// "sponsorApproveTimeout" + /// "ownerCanTransfer", + /// "ownerCanDestroy", + /// "transfersEnabled" + /// 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) { + require(false, stub_error); + dummy; + return new Tuple32[](0); + } + /// Set limits for the collection. /// @dev Throws error if limit not found. /// @param limit Name of the limit. Valid names: @@ -304,12 +329,18 @@ /// "ownerCanTransfer", /// "ownerCanDestroy", /// "transfersEnabled" + /// @param status enable\disable limit. Works only with `true`. /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x4ad890a8, - /// or in textual repr: setCollectionLimit(string,uint256) - function setCollectionLimit(string memory limit, uint256 value) public { + /// @dev EVM selector for this function is: 0x88150bd0, + /// or in textual repr: setCollectionLimit(uint8,bool,uint256) + function setCollectionLimit( + CollectionLimits limit, + bool status, + uint256 value + ) public { require(false, stub_error); limit; + status; value; dummy = 0; } @@ -391,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 (Tuple33 memory) { + function collectionNestingRestrictedCollectionIds() public view returns (Tuple38 memory) { require(false, stub_error); dummy; - return Tuple33(false, new uint256[](0)); + return Tuple38(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 (Tuple36[] memory) { + function collectionNestingPermissions() public view returns (Tuple41[] memory) { require(false, stub_error); dummy; - return new Tuple36[](0); + return new Tuple41[](0); } /// Set the collection access method. @@ -583,17 +614,46 @@ } /// @dev anonymous struct -struct Tuple36 { +struct Tuple41 { CollectionPermissions field_0; bool field_1; } /// @dev anonymous struct -struct Tuple33 { +struct Tuple38 { bool field_0; uint256[] field_1; } +/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. +enum CollectionLimits { + /// @dev How many tokens can a user have on one account. + AccountTokenOwnership, + /// @dev How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// @dev How many tokens can be mined into this collection. + TokenLimit, + /// @dev Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// @dev Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// @dev Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// @dev Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// @dev anonymous struct +struct Tuple32 { + CollectionLimits field_0; + bool field_1; + uint256 field_2; +} + /// @dev anonymous struct struct Tuple29 { address field_0; --- a/tests/src/eth/abi/fungible.json +++ b/tests/src/eth/abi/fungible.json @@ -208,6 +208,28 @@ }, { "inputs": [], + "name": "collectionLimits", + "outputs": [ + { + "components": [ + { + "internalType": "enum CollectionLimits", + "name": "field_0", + "type": "uint8" + }, + { "internalType": "bool", "name": "field_1", "type": "bool" }, + { "internalType": "uint256", "name": "field_2", "type": "uint256" } + ], + "internalType": "struct Tuple20[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], "name": "collectionNestingPermissions", "outputs": [ { @@ -219,7 +241,7 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple24[]", + "internalType": "struct Tuple29[]", "name": "", "type": "tuple[]" } @@ -240,7 +262,7 @@ "type": "uint256[]" } ], - "internalType": "struct Tuple21", + "internalType": "struct Tuple26", "name": "", "type": "tuple" } @@ -453,7 +475,12 @@ }, { "inputs": [ - { "internalType": "string", "name": "limit", "type": "string" }, + { + "internalType": "enum CollectionLimits", + "name": "limit", + "type": "uint8" + }, + { "internalType": "bool", "name": "status", "type": "bool" }, { "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "setCollectionLimit", --- a/tests/src/eth/abi/nonFungible.json +++ b/tests/src/eth/abi/nonFungible.json @@ -238,6 +238,28 @@ }, { "inputs": [], + "name": "collectionLimits", + "outputs": [ + { + "components": [ + { + "internalType": "enum CollectionLimits", + "name": "field_0", + "type": "uint8" + }, + { "internalType": "bool", "name": "field_1", "type": "bool" }, + { "internalType": "uint256", "name": "field_2", "type": "uint256" } + ], + "internalType": "struct Tuple33[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], "name": "collectionNestingPermissions", "outputs": [ { @@ -249,7 +271,7 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple37[]", + "internalType": "struct Tuple42[]", "name": "", "type": "tuple[]" } @@ -270,7 +292,7 @@ "type": "uint256[]" } ], - "internalType": "struct Tuple34", + "internalType": "struct Tuple39", "name": "", "type": "tuple" } @@ -607,7 +629,12 @@ }, { "inputs": [ - { "internalType": "string", "name": "limit", "type": "string" }, + { + "internalType": "enum CollectionLimits", + "name": "limit", + "type": "uint8" + }, + { "internalType": "bool", "name": "status", "type": "bool" }, { "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "setCollectionLimit", @@ -709,12 +736,12 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple46[]", + "internalType": "struct Tuple57[]", "name": "field_1", "type": "tuple[]" } ], - "internalType": "struct Tuple48[]", + "internalType": "struct Tuple59[]", "name": "permissions", "type": "tuple[]" } @@ -775,12 +802,12 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple46[]", + "internalType": "struct Tuple57[]", "name": "field_1", "type": "tuple[]" } ], - "internalType": "struct Tuple48[]", + "internalType": "struct Tuple59[]", "name": "", "type": "tuple[]" } --- a/tests/src/eth/abi/reFungible.json +++ b/tests/src/eth/abi/reFungible.json @@ -220,6 +220,28 @@ }, { "inputs": [], + "name": "collectionLimits", + "outputs": [ + { + "components": [ + { + "internalType": "enum CollectionLimits", + "name": "field_0", + "type": "uint8" + }, + { "internalType": "bool", "name": "field_1", "type": "bool" }, + { "internalType": "uint256", "name": "field_2", "type": "uint256" } + ], + "internalType": "struct Tuple32[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], "name": "collectionNestingPermissions", "outputs": [ { @@ -231,7 +253,7 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple36[]", + "internalType": "struct Tuple41[]", "name": "", "type": "tuple[]" } @@ -252,7 +274,7 @@ "type": "uint256[]" } ], - "internalType": "struct Tuple33", + "internalType": "struct Tuple38", "name": "", "type": "tuple" } @@ -589,7 +611,12 @@ }, { "inputs": [ - { "internalType": "string", "name": "limit", "type": "string" }, + { + "internalType": "enum CollectionLimits", + "name": "limit", + "type": "uint8" + }, + { "internalType": "bool", "name": "status", "type": "bool" }, { "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "setCollectionLimit", @@ -691,12 +718,12 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple51[]", + "internalType": "struct Tuple56[]", "name": "field_1", "type": "tuple[]" } ], - "internalType": "struct Tuple53[]", + "internalType": "struct Tuple58[]", "name": "permissions", "type": "tuple[]" } @@ -766,12 +793,12 @@ }, { "internalType": "bool", "name": "field_1", "type": "bool" } ], - "internalType": "struct Tuple51[]", + "internalType": "struct Tuple56[]", "name": "field_1", "type": "tuple[]" } ], - "internalType": "struct Tuple53[]", + "internalType": "struct Tuple58[]", "name": "", "type": "tuple[]" } --- a/tests/src/eth/api/UniqueFungible.sol +++ b/tests/src/eth/api/UniqueFungible.sol @@ -13,7 +13,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xb5e1747f +/// @dev the ERC-165 identifier for this interface is 0x81172a75 interface Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -104,6 +104,23 @@ /// or in textual repr: collectionSponsor() function collectionSponsor() external view returns (Tuple8 memory); + /// Get current collection limits. + /// + /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: + /// "accountTokenOwnershipLimit", + /// "sponsoredDataSize", + /// "sponsoredDataRateLimit", + /// "tokenLimit", + /// "sponsorTransferTimeout", + /// "sponsorApproveTimeout" + /// "ownerCanTransfer", + /// "ownerCanDestroy", + /// "transfersEnabled" + /// 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); + /// Set limits for the collection. /// @dev Throws error if limit not found. /// @param limit Name of the limit. Valid names: @@ -116,10 +133,15 @@ /// "ownerCanTransfer", /// "ownerCanDestroy", /// "transfersEnabled" + /// @param status enable\disable limit. Works only with `true`. /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x4ad890a8, - /// or in textual repr: setCollectionLimit(string,uint256) - function setCollectionLimit(string memory limit, uint256 value) external; + /// @dev EVM selector for this function is: 0x88150bd0, + /// or in textual repr: setCollectionLimit(uint8,bool,uint256) + function setCollectionLimit( + CollectionLimits limit, + bool status, + uint256 value + ) external; /// Get contract address. /// @dev EVM selector for this function is: 0xf6b4dfb4, @@ -169,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 (Tuple20 memory); + function collectionNestingRestrictedCollectionIds() external view returns (Tuple24 memory); /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() external view returns (Tuple23[] memory); + function collectionNestingPermissions() external view returns (Tuple27[] memory); /// Set the collection access method. /// @param mode Access mode @@ -289,7 +311,7 @@ } /// @dev anonymous struct -struct Tuple23 { +struct Tuple27 { CollectionPermissions field_0; bool field_1; } @@ -300,11 +322,40 @@ } /// @dev anonymous struct -struct Tuple20 { +struct Tuple24 { bool field_0; uint256[] field_1; } +/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. +enum CollectionLimits { + /// @dev How many tokens can a user have on one account. + AccountTokenOwnership, + /// @dev How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// @dev How many tokens can be mined into this collection. + TokenLimit, + /// @dev Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// @dev Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// @dev Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// @dev Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// @dev anonymous struct +struct Tuple19 { + CollectionLimits field_0; + bool field_1; + uint256 field_2; +} + /// @dev Property struct struct Property { string key; --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -30,11 +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(Tuple43[] 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 (Tuple43[] 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. @@ -85,26 +86,30 @@ bytes value; } +/// @dev Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. enum EthTokenPermissions { + /// @dev Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] Mutable, + /// @dev Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] TokenOwner, + /// @dev Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] CollectionAdmin } /// @dev anonymous struct -struct Tuple43 { +struct Tuple52 { string field_0; - Tuple41[] field_1; + Tuple50[] field_1; } /// @dev anonymous struct -struct Tuple41 { +struct Tuple50 { EthTokenPermissions field_0; bool field_1; } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xb5e1747f +/// @dev the ERC-165 identifier for this interface is 0x81172a75 interface Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -195,6 +200,23 @@ /// or in textual repr: collectionSponsor() function collectionSponsor() external view returns (Tuple27 memory); + /// Get current collection limits. + /// + /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: + /// "accountTokenOwnershipLimit", + /// "sponsoredDataSize", + /// "sponsoredDataRateLimit", + /// "tokenLimit", + /// "sponsorTransferTimeout", + /// "sponsorApproveTimeout" + /// "ownerCanTransfer", + /// "ownerCanDestroy", + /// "transfersEnabled" + /// 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); + /// Set limits for the collection. /// @dev Throws error if limit not found. /// @param limit Name of the limit. Valid names: @@ -207,10 +229,15 @@ /// "ownerCanTransfer", /// "ownerCanDestroy", /// "transfersEnabled" + /// @param status enable\disable limit. Works only with `true`. /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x4ad890a8, - /// or in textual repr: setCollectionLimit(string,uint256) - function setCollectionLimit(string memory limit, uint256 value) external; + /// @dev EVM selector for this function is: 0x88150bd0, + /// or in textual repr: setCollectionLimit(uint8,bool,uint256) + function setCollectionLimit( + CollectionLimits limit, + bool status, + uint256 value + ) external; /// Get contract address. /// @dev EVM selector for this function is: 0xf6b4dfb4, @@ -260,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 (Tuple31 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 (Tuple34[] memory); + function collectionNestingPermissions() external view returns (Tuple38[] memory); /// Set the collection access method. /// @param mode Access mode @@ -380,7 +407,7 @@ } /// @dev anonymous struct -struct Tuple34 { +struct Tuple38 { CollectionPermissions field_0; bool field_1; } @@ -391,11 +418,40 @@ } /// @dev anonymous struct -struct Tuple31 { +struct Tuple35 { bool field_0; uint256[] field_1; } +/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. +enum CollectionLimits { + /// @dev How many tokens can a user have on one account. + AccountTokenOwnership, + /// @dev How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// @dev How many tokens can be mined into this collection. + TokenLimit, + /// @dev Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// @dev Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// @dev Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// @dev Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// @dev anonymous struct +struct Tuple30 { + CollectionLimits field_0; + bool field_1; + uint256 field_2; +} + /// @dev anonymous struct struct Tuple27 { address field_0; --- 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(Tuple47[] memory permissions) external; + function setTokenPropertyPermissions(Tuple51[] 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 (Tuple47[] memory); + function tokenPropertyPermissions() external view returns (Tuple51[] memory); // /// @notice Set token property value. // /// @dev Throws error if `msg.sender` has no permission to edit the property. @@ -86,26 +86,30 @@ bytes value; } +/// @dev Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. enum EthTokenPermissions { + /// @dev Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] Mutable, + /// @dev Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] TokenOwner, + /// @dev Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] CollectionAdmin } /// @dev anonymous struct -struct Tuple47 { +struct Tuple51 { string field_0; - Tuple45[] field_1; + Tuple49[] field_1; } /// @dev anonymous struct -struct Tuple45 { +struct Tuple49 { EthTokenPermissions field_0; bool field_1; } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xb5e1747f +/// @dev the ERC-165 identifier for this interface is 0x81172a75 interface Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -196,6 +200,23 @@ /// or in textual repr: collectionSponsor() function collectionSponsor() external view returns (Tuple26 memory); + /// Get current collection limits. + /// + /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: + /// "accountTokenOwnershipLimit", + /// "sponsoredDataSize", + /// "sponsoredDataRateLimit", + /// "tokenLimit", + /// "sponsorTransferTimeout", + /// "sponsorApproveTimeout" + /// "ownerCanTransfer", + /// "ownerCanDestroy", + /// "transfersEnabled" + /// 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); + /// Set limits for the collection. /// @dev Throws error if limit not found. /// @param limit Name of the limit. Valid names: @@ -208,10 +229,15 @@ /// "ownerCanTransfer", /// "ownerCanDestroy", /// "transfersEnabled" + /// @param status enable\disable limit. Works only with `true`. /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x4ad890a8, - /// or in textual repr: setCollectionLimit(string,uint256) - function setCollectionLimit(string memory limit, uint256 value) external; + /// @dev EVM selector for this function is: 0x88150bd0, + /// or in textual repr: setCollectionLimit(uint8,bool,uint256) + function setCollectionLimit( + CollectionLimits limit, + bool status, + uint256 value + ) external; /// Get contract address. /// @dev EVM selector for this function is: 0xf6b4dfb4, @@ -261,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 (Tuple30 memory); + function collectionNestingRestrictedCollectionIds() external view returns (Tuple34 memory); /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() external view returns (Tuple33[] memory); + function collectionNestingPermissions() external view returns (Tuple37[] memory); /// Set the collection access method. /// @param mode Access mode @@ -381,7 +407,7 @@ } /// @dev anonymous struct -struct Tuple33 { +struct Tuple37 { CollectionPermissions field_0; bool field_1; } @@ -392,11 +418,40 @@ } /// @dev anonymous struct -struct Tuple30 { +struct Tuple34 { bool field_0; uint256[] field_1; } +/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. +enum CollectionLimits { + /// @dev How many tokens can a user have on one account. + AccountTokenOwnership, + /// @dev How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// @dev How many tokens can be mined into this collection. + TokenLimit, + /// @dev Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// @dev Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// @dev Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// @dev Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// @dev anonymous struct +struct Tuple29 { + CollectionLimits field_0; + bool field_1; + uint256 field_2; +} + /// @dev anonymous struct struct Tuple26 { address field_0; --- a/tests/src/eth/collectionLimits.test.ts +++ b/tests/src/eth/collectionLimits.test.ts @@ -1,6 +1,7 @@ import {IKeyringPair} from '@polkadot/types/types'; import {Pallets} from '../util'; import {expect, itEth, usingEthPlaygrounds} from './util'; +import {CollectionLimits} from './util/playgrounds/types'; describe('Can set collection limits', () => { @@ -44,20 +45,33 @@ transfersEnabled: false, }; - const collection = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); - await collection.methods.setCollectionLimit('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); - await collection.methods.setCollectionLimit('sponsoredDataSize', limits.sponsoredDataSize).send(); - await collection.methods.setCollectionLimit('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); - await collection.methods.setCollectionLimit('tokenLimit', limits.tokenLimit).send(); - await collection.methods.setCollectionLimit('sponsorTransferTimeout', limits.sponsorTransferTimeout).send(); - await collection.methods.setCollectionLimit('sponsorApproveTimeout', limits.sponsorApproveTimeout).send(); - await collection.methods.setCollectionLimit('ownerCanTransfer', limits.ownerCanTransfer).send(); - await collection.methods.setCollectionLimit('ownerCanDestroy', limits.ownerCanDestroy).send(); - await collection.methods.setCollectionLimit('transfersEnabled', limits.transfersEnabled).send(); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); + await collectionEvm.methods.setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, limits.accountTokenOwnershipLimit).send(); + await collectionEvm.methods.setCollectionLimit(CollectionLimits.SponsoredDataSize, true, limits.sponsoredDataSize).send(); + await collectionEvm.methods.setCollectionLimit(CollectionLimits.SponsoredDataRateLimit, true, limits.sponsoredDataRateLimit).send(); + await collectionEvm.methods.setCollectionLimit(CollectionLimits.TokenLimit, true, limits.tokenLimit).send(); + await collectionEvm.methods.setCollectionLimit(CollectionLimits.SponsorTransferTimeout, true, limits.sponsorTransferTimeout).send(); + await collectionEvm.methods.setCollectionLimit(CollectionLimits.SponsorApproveTimeout, true, limits.sponsorApproveTimeout).send(); + await collectionEvm.methods.setCollectionLimit(CollectionLimits.OwnerCanTransfer, true, limits.ownerCanTransfer).send(); + await collectionEvm.methods.setCollectionLimit(CollectionLimits.OwnerCanDestroy, true, limits.ownerCanDestroy).send(); + await collectionEvm.methods.setCollectionLimit(CollectionLimits.TransferEnabled, true, limits.transfersEnabled).send(); + // Check limits from sub: const data = (await helper.rft.getData(collectionId))!; expect(data.raw.limits).to.deep.eq(expectedLimits); expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits); + // Check limits from eth: + const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner}); + expect(limitsEvm).to.have.length(9); + expect(limitsEvm[0]).to.deep.eq([CollectionLimits.AccountTokenOwnership.toString(), true, limits.accountTokenOwnershipLimit.toString()]); + expect(limitsEvm[1]).to.deep.eq([CollectionLimits.SponsoredDataSize.toString(), true, limits.sponsoredDataSize.toString()]); + expect(limitsEvm[2]).to.deep.eq([CollectionLimits.SponsoredDataRateLimit.toString(), true, limits.sponsoredDataRateLimit.toString()]); + expect(limitsEvm[3]).to.deep.eq([CollectionLimits.TokenLimit.toString(), true, limits.tokenLimit.toString()]); + expect(limitsEvm[4]).to.deep.eq([CollectionLimits.SponsorTransferTimeout.toString(), true, limits.sponsorTransferTimeout.toString()]); + expect(limitsEvm[5]).to.deep.eq([CollectionLimits.SponsorApproveTimeout.toString(), true, limits.sponsorApproveTimeout.toString()]); + expect(limitsEvm[6]).to.deep.eq([CollectionLimits.OwnerCanTransfer.toString(), true, limits.ownerCanTransfer.toString()]); + expect(limitsEvm[7]).to.deep.eq([CollectionLimits.OwnerCanDestroy.toString(), true, limits.ownerCanDestroy.toString()]); + expect(limitsEvm[8]).to.deep.eq([CollectionLimits.TransferEnabled.toString(), true, limits.transfersEnabled.toString()]); })); }); @@ -84,17 +98,48 @@ const owner = await helper.eth.createAccountWithBalance(donor); const {collectionAddress} = await helper.eth.createCollection(testCase.case, owner, 'Limits', 'absolutely anything', 'ISNI', 18); const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); + + // Cannot set non-existing limit await expect(collectionEvm.methods - .setCollectionLimit('badLimit', '1') - .call()).to.be.rejectedWith('unknown limit "badLimit"'); - + .setCollectionLimit(9, true, 1) + .call()).to.be.rejectedWith('Returned error: VM Exception while processing transaction: revert Value not convertible into enum "CollectionLimits"'); + + // Cannot disable limits + await expect(collectionEvm.methods + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, false, 200) + .call()).to.be.rejectedWith('Returned error: VM Exception while processing transaction: revert user can\'t disable limits'); + await expect(collectionEvm.methods - .setCollectionLimit(Object.keys(invalidLimits)[0], invalidLimits.accountTokenOwnershipLimit) + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, invalidLimits.accountTokenOwnershipLimit) .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); - + await expect(collectionEvm.methods - .setCollectionLimit(Object.keys(invalidLimits)[1], invalidLimits.transfersEnabled) + .setCollectionLimit(CollectionLimits.TransferEnabled, true, 3) .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); + + expect(() => collectionEvm.methods + .setCollectionLimit(CollectionLimits.SponsoredDataSize, true, -1).send()).to.throw('value out-of-bounds'); })); + + [ + {case: 'nft' as const, requiredPallets: []}, + {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {case: 'ft' as const, requiredPallets: []}, + ].map(testCase => + itEth.ifWithPallets(`Non-owner and non-admin cannot set collection limits for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const nonOwner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection(testCase.case, owner, 'Limits', 'absolutely anything', 'FLO', 18); + + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); + await expect(collectionEvm.methods + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .call({from: nonOwner})) + .to.be.rejectedWith('NoPermission'); + + await expect(collectionEvm.methods + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .send({from: nonOwner})) + .to.be.rejected; + })); }); - \ No newline at end of file --- a/tests/src/eth/createFTCollection.test.ts +++ b/tests/src/eth/createFTCollection.test.ts @@ -18,6 +18,7 @@ import {evmToAddress} from '@polkadot/util-crypto'; import {Pallets, requirePalletsOrSkip} from '../util'; import {expect, itEth, usingEthPlaygrounds} from './util'; +import { CollectionLimits } from './util/playgrounds/types'; const DECIMALS = 18; @@ -196,7 +197,7 @@ } { await expect(peasantCollection.methods - .setCollectionLimit('accountTokenOwnershipLimit', '1000') + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); @@ -221,7 +222,7 @@ } { await expect(peasantCollection.methods - .setCollectionLimit('accountTokenOwnershipLimit', '1000') + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); --- a/tests/src/eth/createNFTCollection.test.ts +++ b/tests/src/eth/createNFTCollection.test.ts @@ -17,6 +17,7 @@ import {evmToAddress} from '@polkadot/util-crypto'; import {IKeyringPair} from '@polkadot/types/types'; import {expect, itEth, usingEthPlaygrounds} from './util'; +import { CollectionLimits } from './util/playgrounds/types'; describe('Create NFT collection from EVM', () => { @@ -207,7 +208,7 @@ } { await expect(malfeasantCollection.methods - .setCollectionLimit('accountTokenOwnershipLimit', '1000') + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); @@ -232,7 +233,7 @@ } { await expect(malfeasantCollection.methods - .setCollectionLimit('accountTokenOwnershipLimit', '1000') + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); --- a/tests/src/eth/createRFTCollection.test.ts +++ b/tests/src/eth/createRFTCollection.test.ts @@ -18,6 +18,7 @@ import {IKeyringPair} from '@polkadot/types/types'; import {Pallets, requirePalletsOrSkip} from '../util'; import {expect, itEth, usingEthPlaygrounds} from './util'; +import {CollectionLimits} from './util/playgrounds/types'; describe('Create RFT collection from EVM', () => { @@ -239,7 +240,7 @@ } { await expect(peasantCollection.methods - .setCollectionLimit('accountTokenOwnershipLimit', '1000') + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); @@ -264,7 +265,7 @@ } { await expect(peasantCollection.methods - .setCollectionLimit('accountTokenOwnershipLimit', '1000') + .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); --- a/tests/src/eth/events.test.ts +++ b/tests/src/eth/events.test.ts @@ -19,7 +19,7 @@ import {EthUniqueHelper, itEth, usingEthPlaygrounds} from './util'; import {IEvent, TCollectionMode} from '../util/playgrounds/types'; import {Pallets, requirePalletsOrSkip} from '../util'; -import {EthTokenPermissions, NormalizedEvent} from './util/playgrounds/types'; +import {CollectionLimits, EthTokenPermissions, NormalizedEvent} from './util/playgrounds/types'; let donor: IKeyringPair; @@ -234,7 +234,7 @@ }); const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionLimitSet']}]); { - await collection.methods.setCollectionLimit('ownerCanTransfer', 0n).send({from: owner}); + await collection.methods.setCollectionLimit(CollectionLimits.OwnerCanTransfer, true, 0).send({from: owner}); await helper.wait.newBlocks(1); expect(ethEvents).to.be.like([ { --- a/tests/src/eth/util/playgrounds/types.ts +++ b/tests/src/eth/util/playgrounds/types.ts @@ -24,4 +24,15 @@ Mutable, TokenOwner, CollectionAdmin -} \ No newline at end of file +} +export enum CollectionLimits { + AccountTokenOwnership, + SponsoredDataSize, + SponsoredDataRateLimit, + TokenLimit, + SponsorTransferTimeout, + SponsorApproveTimeout, + OwnerCanTransfer, + OwnerCanDestroy, + TransferEnabled +} --- a/tests/src/pallet-presence.test.ts +++ b/tests/src/pallet-presence.test.ts @@ -84,6 +84,7 @@ ); } else if (chain.eq('UNIQUE')) { // Insert Unique additional pallets here + requiredPallets.push(foreignAssets); } }); });