From bf0e967f70e33931cca3fde8c6bfcd55ad625449 Mon Sep 17 00:00:00 2001 From: Trubnikov Sergey Date: Wed, 14 Dec 2022 12:50:15 +0000 Subject: [PATCH] feat: set/get tokenPropertyPermissions for NFT --- --- a/pallets/common/src/eth.rs +++ b/pallets/common/src/eth.rs @@ -162,3 +162,12 @@ CollectionAdmin, TokenOwner, } + +#[derive(AbiCoder, Copy, Clone, Default, Debug)] +#[repr(u8)] +pub enum EthTokenPermissions { + #[default] + Mutable, + TokenOwner, + CollectionAdmin, +} --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -38,7 +38,7 @@ use pallet_common::{ CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations, erc::{CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key}, - eth::EthCrossAccount, + eth::{EthCrossAccount, EthTokenPermissions}, }; use pallet_evm::{account::CrossAccountId, PrecompileHandle}; use pallet_evm_coder_substrate::call; @@ -60,6 +60,7 @@ /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. /// @param tokenOwner Permission to mutate property by token owner if property is mutable. #[weight(>::set_token_property_permissions(1))] + #[solidity(hide)] fn set_token_property_permission( &mut self, caller: caller, @@ -69,10 +70,10 @@ token_owner: bool, ) -> Result<()> { let caller = T::CrossAccountId::from_eth(caller); - >::set_property_permission( + >::set_property_permissions( self, &caller, - PropertyKeyPermission { + [PropertyKeyPermission { key: >::from(key) .try_into() .map_err(|_| "too long key")?, @@ -81,11 +82,81 @@ collection_admin, token_owner, }, - }, + }] + .into(), ) .map_err(dispatch_to_evm::) } + /// @notice Set permissions for token property. + /// @dev Throws error if `msg.sender` is not admin or owner of the collection. + /// @param permissions Permissions for keys. + fn set_token_property_permissions( + &mut self, + caller: caller, + permissions: Vec<(string, Vec<(EthTokenPermissions, bool)>)>, + ) -> Result<()> { + let caller = T::CrossAccountId::from_eth(caller); + const PERMISSIONS_FIELDS_COUNT: usize = 3; + + let mut perms = >::new(); + + for (key, pp) in permissions { + if pp.len() > PERMISSIONS_FIELDS_COUNT { + return Err(alloc::format!( + "Actual number of fields {} for {}, which exceeds the maximum value of {}", + pp.len(), + stringify!(EthTokenPermissions), + PERMISSIONS_FIELDS_COUNT + ) + .as_str() + .into()); + } + + let mut token_permission = PropertyPermission { + mutable: false, + collection_admin: false, + token_owner: false, + }; + + for (perm, value) in pp { + match perm { + EthTokenPermissions::Mutable => token_permission.mutable = value, + EthTokenPermissions::TokenOwner => token_permission.token_owner = value, + EthTokenPermissions::CollectionAdmin => { + token_permission.collection_admin = value + } + } + } + + perms.push(PropertyKeyPermission { + key: >::from(key) + .try_into() + .map_err(|_| "too long key")?, + permission: token_permission, + }); + } + + >::set_property_permissions(self, &caller, perms).map_err(dispatch_to_evm::) + } + + fn token_property_permissions( + &self, + ) -> Result)>> { + let mut res = >::new(); + for (key, pp) in >::token_property_permission(self.id) { + let key = string::from_utf8(key.into_inner()).unwrap(); + let pp = [ + (EthTokenPermissions::Mutable, pp.mutable), + (EthTokenPermissions::TokenOwner, pp.token_owner), + (EthTokenPermissions::CollectionAdmin, pp.collection_admin), + ] + .into(); + res.push((key, pp)); + } + Ok(res) + } + /// @notice Set token property value. /// @dev Throws error if `msg.sender` has no permission to edit the property. /// @param tokenId ID of the token. --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -103,7 +103,7 @@ AccessMode, CollectionId, CollectionFlags, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData, mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, PropertyKey, PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, - TokenChild, AuxPropertyValue, + TokenChild, AuxPropertyValue, PropertiesPermissionMap, }; use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm}; use pallet_common::{ @@ -827,12 +827,16 @@ /// Set property permissions for the collection. /// /// Sender should be the owner or admin of the collection. - pub fn set_property_permission( + pub fn set_property_permissions( collection: &CollectionHandle, sender: &T::CrossAccountId, - permission: PropertyKeyPermission, + permission: Vec, ) -> DispatchResult { - >::set_property_permission(collection, sender, permission) + >::set_token_property_permissions(collection, sender, permission) + } + + pub fn token_property_permission(collection_id: CollectionId) -> PropertiesPermissionMap { + >::property_permissions(collection_id) } pub fn check_token_immediate_ownership( --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -18,30 +18,44 @@ } /// @title A contract that allows to set and delete token properties and change token property permissions. -/// @dev the ERC-165 identifier for this interface is 0x91a97a68 +/// @dev the ERC-165 identifier for this interface is 0xde0695c2 contract TokenProperties is Dummy, ERC165 { + // /// @notice Set permissions for token property. + // /// @dev Throws error if `msg.sender` is not admin or owner of the collection. + // /// @param key Property key. + // /// @param isMutable Permission to mutate property. + // /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. + // /// @param tokenOwner Permission to mutate property by token owner if property is mutable. + // /// @dev EVM selector for this function is: 0x222d97fa, + // /// or in textual repr: setTokenPropertyPermission(string,bool,bool,bool) + // function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) public { + // require(false, stub_error); + // key; + // isMutable; + // collectionAdmin; + // tokenOwner; + // dummy = 0; + // } + /// @notice Set permissions for token property. /// @dev Throws error if `msg.sender` is not admin or owner of the collection. - /// @param key Property key. - /// @param isMutable Permission to mutate property. - /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. - /// @param tokenOwner Permission to mutate property by token owner if property is mutable. - /// @dev EVM selector for this function is: 0x222d97fa, - /// or in textual repr: setTokenPropertyPermission(string,bool,bool,bool) - function setTokenPropertyPermission( - string memory key, - bool isMutable, - bool collectionAdmin, - bool tokenOwner - ) public { + /// @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 { require(false, stub_error); - key; - isMutable; - collectionAdmin; - tokenOwner; + permissions; dummy = 0; } + /// @dev EVM selector for this function is: 0xf23d7790, + /// or in textual repr: tokenPropertyPermissions() + function tokenPropertyPermissions() public view returns (Tuple48[] memory) { + require(false, stub_error); + dummy; + return new Tuple48[](0); + } + // /// @notice Set token property value. // /// @dev Throws error if `msg.sender` has no permission to edit the property. // /// @param tokenId ID of the token. @@ -118,6 +132,24 @@ bytes value; } +enum EthTokenPermissions { + Mutable, + TokenOwner, + CollectionAdmin +} + +/// @dev anonymous struct +struct Tuple48 { + string field_0; + Tuple46[] field_1; +} + +/// @dev anonymous struct +struct Tuple46 { + 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 contract Collection is Dummy, ERC165 { --- a/tests/src/eth/abi/nonFungible.json +++ b/tests/src/eth/abi/nonFungible.json @@ -697,12 +697,29 @@ }, { "inputs": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bool", "name": "isMutable", "type": "bool" }, - { "internalType": "bool", "name": "collectionAdmin", "type": "bool" }, - { "internalType": "bool", "name": "tokenOwner", "type": "bool" } + { + "components": [ + { "internalType": "string", "name": "field_0", "type": "string" }, + { + "components": [ + { + "internalType": "enum EthTokenPermissions", + "name": "field_0", + "type": "uint8" + }, + { "internalType": "bool", "name": "field_1", "type": "bool" } + ], + "internalType": "struct Tuple46[]", + "name": "field_1", + "type": "tuple[]" + } + ], + "internalType": "struct Tuple48[]", + "name": "permissions", + "type": "tuple[]" + } ], - "name": "setTokenPropertyPermission", + "name": "setTokenPropertyPermissions", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -743,6 +760,35 @@ "type": "function" }, { + "inputs": [], + "name": "tokenPropertyPermissions", + "outputs": [ + { + "components": [ + { "internalType": "string", "name": "field_0", "type": "string" }, + { + "components": [ + { + "internalType": "enum EthTokenPermissions", + "name": "field_0", + "type": "uint8" + }, + { "internalType": "bool", "name": "field_1", "type": "bool" } + ], + "internalType": "struct Tuple46[]", + "name": "field_1", + "type": "tuple[]" + } + ], + "internalType": "struct Tuple48[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [ { "internalType": "uint256", "name": "tokenId", "type": "uint256" } ], --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -13,22 +13,28 @@ } /// @title A contract that allows to set and delete token properties and change token property permissions. -/// @dev the ERC-165 identifier for this interface is 0x91a97a68 +/// @dev the ERC-165 identifier for this interface is 0xde0695c2 interface TokenProperties is Dummy, ERC165 { + // /// @notice Set permissions for token property. + // /// @dev Throws error if `msg.sender` is not admin or owner of the collection. + // /// @param key Property key. + // /// @param isMutable Permission to mutate property. + // /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. + // /// @param tokenOwner Permission to mutate property by token owner if property is mutable. + // /// @dev EVM selector for this function is: 0x222d97fa, + // /// or in textual repr: setTokenPropertyPermission(string,bool,bool,bool) + // function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external; + /// @notice Set permissions for token property. /// @dev Throws error if `msg.sender` is not admin or owner of the collection. - /// @param key Property key. - /// @param isMutable Permission to mutate property. - /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. - /// @param tokenOwner Permission to mutate property by token owner if property is mutable. - /// @dev EVM selector for this function is: 0x222d97fa, - /// or in textual repr: setTokenPropertyPermission(string,bool,bool,bool) - function setTokenPropertyPermission( - string memory key, - bool isMutable, - bool collectionAdmin, - bool tokenOwner - ) external; + /// @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; + + /// @dev EVM selector for this function is: 0xf23d7790, + /// or in textual repr: tokenPropertyPermissions() + function tokenPropertyPermissions() external view returns (Tuple43[] memory); // /// @notice Set token property value. // /// @dev Throws error if `msg.sender` has no permission to edit the property. @@ -79,6 +85,24 @@ bytes value; } +enum EthTokenPermissions { + Mutable, + TokenOwner, + CollectionAdmin +} + +/// @dev anonymous struct +struct Tuple43 { + string field_0; + Tuple41[] field_1; +} + +/// @dev anonymous struct +struct Tuple41 { + 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 interface Collection is Dummy, ERC165 { --- a/tests/src/eth/tokenProperties.test.ts +++ b/tests/src/eth/tokenProperties.test.ts @@ -32,7 +32,7 @@ }); }); - itEth('Can be reconfigured', async({helper}) => { + itEth.only('Can be reconfigured', async({helper}) => { const caller = await helper.eth.createAccountWithBalance(donor); for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) { const collection = await helper.nft.mintCollection(alice); @@ -41,12 +41,16 @@ const address = helper.ethAddress.fromCollectionId(collection.collectionId); const contract = helper.ethNativeContract.collection(address, 'nft', caller); - await contract.methods.setTokenPropertyPermission('testKey', mutable, collectionAdmin, tokenOwner).send({from: caller}); + await contract.methods.setTokenPropertyPermissions([['testKey', [[0, mutable], [1, tokenOwner], [2, collectionAdmin]]]]).send({from: caller}); expect(await collection.getPropertyPermissions()).to.be.deep.equal([{ key: 'testKey', permission: {mutable, collectionAdmin, tokenOwner}, }]); + + expect(await contract.methods.tokenPropertyPermissions().call({from: caller})).to.be.like([ + ['testKey', [['0', mutable], ['1', tokenOwner], ['2', collectionAdmin]]] + ]); } }); -- gitstuff