From 36fc5abfd9d0fe7c7b8922505df17a7dfac1e2fc Mon Sep 17 00:00:00 2001 From: Trubnikov Sergey Date: Mon, 24 Oct 2022 21:43:48 +0000 Subject: [PATCH] feat: add delete properties --- --- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -117,8 +117,6 @@ /// @param key Property key. #[weight(>::delete_collection_properties(1))] fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> { - self.consume_store_reads_and_writes(1, 1)?; - let caller = T::CrossAccountId::from_eth(caller); let key = >::from(key) .try_into() @@ -127,6 +125,24 @@ >::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::) } + /// Delete collection properties. + /// + /// @param keys Properties keys. + #[weight(>::delete_collection_properties(keys.len() as u32))] + fn delete_collection_properties(&mut self, caller: caller, keys: Vec) -> Result<()> { + let caller = T::CrossAccountId::from_eth(caller); + let keys = keys + .into_iter() + .map(|key| { + >::from(key) + .try_into() + .map_err(|_| Error::Revert("key too large".into())) + }) + .collect::>>()?; + + >::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::) + } + /// Get collection property. /// /// @dev Throws error if key not found. @@ -146,28 +162,34 @@ /// Get collection properties. /// - /// @param keys Properties keys. + /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. fn collection_properties(&self, keys: Vec) -> Result> { - let mut keys_ = Vec::::with_capacity(keys.len()); - for key in keys { - keys_.push( + let keys = keys + .into_iter() + .map(|key| { >::from(key) .try_into() - .map_err(|_| Error::Revert("key too large".into()))?, - ) - } - let properties = Pallet::::filter_collection_properties(self.id, Some(keys_)) - .map_err(dispatch_to_evm::)?; + .map_err(|_| Error::Revert("key too large".into())) + }) + .collect::>>()?; - let mut properties_ = Vec::<(string, bytes)>::with_capacity(properties.len()); - for p in properties { - let key = - string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?; - let value = bytes(p.value.to_vec()); - properties_.push((key, value)); - } - Ok(properties_) + let properties = Pallet::::filter_collection_properties( + self.id, + if keys.is_empty() { None } else { Some(keys) }, + ) + .map_err(dispatch_to_evm::)?; + + let properties = properties + .into_iter() + .map(|p| { + let key = + string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?; + let value = bytes(p.value.to_vec()); + Ok((key, value)) + }) + .collect::>>()?; + Ok(properties) } /// Set the sponsor of the collection. --- 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 0x5d354410 +/// @dev the ERC-165 identifier for this interface is 0xb3152af3 contract Collection is Dummy, ERC165 { /// Set collection property. /// @@ -55,6 +55,17 @@ dummy = 0; } + /// Delete collection properties. + /// + /// @param keys Properties keys. + /// @dev EVM selector for this function is: 0xee206ee3, + /// or in textual repr: deleteCollectionProperties(string[]) + function deleteCollectionProperties(string[] memory keys) public { + require(false, stub_error); + keys; + dummy = 0; + } + /// Get collection property. /// /// @dev Throws error if key not found. @@ -72,7 +83,7 @@ /// Get collection properties. /// - /// @param keys Properties keys. + /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -91,7 +91,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x5d354410 +/// @dev the ERC-165 identifier for this interface is 0xb3152af3 contract Collection is Dummy, ERC165 { /// Set collection property. /// @@ -128,6 +128,17 @@ dummy = 0; } + /// Delete collection properties. + /// + /// @param keys Properties keys. + /// @dev EVM selector for this function is: 0xee206ee3, + /// or in textual repr: deleteCollectionProperties(string[]) + function deleteCollectionProperties(string[] memory keys) public { + require(false, stub_error); + keys; + dummy = 0; + } + /// Get collection property. /// /// @dev Throws error if key not found. @@ -145,7 +156,7 @@ /// Get collection properties. /// - /// @param keys Properties keys. + /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) --- a/pallets/refungible/src/stubs/UniqueRefungible.sol +++ b/pallets/refungible/src/stubs/UniqueRefungible.sol @@ -91,7 +91,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x5d354410 +/// @dev the ERC-165 identifier for this interface is 0xb3152af3 contract Collection is Dummy, ERC165 { /// Set collection property. /// @@ -128,6 +128,17 @@ dummy = 0; } + /// Delete collection properties. + /// + /// @param keys Properties keys. + /// @dev EVM selector for this function is: 0xee206ee3, + /// or in textual repr: deleteCollectionProperties(string[]) + function deleteCollectionProperties(string[] memory keys) public { + require(false, stub_error); + keys; + dummy = 0; + } + /// Get collection property. /// /// @dev Throws error if key not found. @@ -145,7 +156,7 @@ /// Get collection properties. /// - /// @param keys Properties keys. + /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) --- 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 0x5d354410 +/// @dev the ERC-165 identifier for this interface is 0xb3152af3 interface Collection is Dummy, ERC165 { /// Set collection property. /// @@ -37,6 +37,13 @@ /// or in textual repr: deleteCollectionProperty(string) function deleteCollectionProperty(string memory key) external; + /// Delete collection properties. + /// + /// @param keys Properties keys. + /// @dev EVM selector for this function is: 0xee206ee3, + /// or in textual repr: deleteCollectionProperties(string[]) + function deleteCollectionProperties(string[] memory keys) external; + /// Get collection property. /// /// @dev Throws error if key not found. @@ -49,7 +56,7 @@ /// Get collection properties. /// - /// @param keys Properties keys. + /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -62,7 +62,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x5d354410 +/// @dev the ERC-165 identifier for this interface is 0xb3152af3 interface Collection is Dummy, ERC165 { /// Set collection property. /// @@ -86,6 +86,13 @@ /// or in textual repr: deleteCollectionProperty(string) function deleteCollectionProperty(string memory key) external; + /// Delete collection properties. + /// + /// @param keys Properties keys. + /// @dev EVM selector for this function is: 0xee206ee3, + /// or in textual repr: deleteCollectionProperties(string[]) + function deleteCollectionProperties(string[] memory keys) external; + /// Get collection property. /// /// @dev Throws error if key not found. @@ -98,7 +105,7 @@ /// Get collection properties. /// - /// @param keys Properties keys. + /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) --- a/tests/src/eth/api/UniqueRefungible.sol +++ b/tests/src/eth/api/UniqueRefungible.sol @@ -62,7 +62,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x5d354410 +/// @dev the ERC-165 identifier for this interface is 0xb3152af3 interface Collection is Dummy, ERC165 { /// Set collection property. /// @@ -86,6 +86,13 @@ /// or in textual repr: deleteCollectionProperty(string) function deleteCollectionProperty(string memory key) external; + /// Delete collection properties. + /// + /// @param keys Properties keys. + /// @dev EVM selector for this function is: 0xee206ee3, + /// or in textual repr: deleteCollectionProperties(string[]) + function deleteCollectionProperties(string[] memory keys) external; + /// Get collection property. /// /// @dev Throws error if key not found. @@ -98,7 +105,7 @@ /// Get collection properties. /// - /// @param keys Properties keys. + /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) --- a/tests/src/eth/collectionProperties.test.ts +++ b/tests/src/eth/collectionProperties.test.ts @@ -16,7 +16,7 @@ import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util'; import {Pallets} from '../util'; -import {IProperty, ITokenPropertyPermission} from '../util/playgrounds/types'; +import {IProperty, ITokenPropertyPermission, TCollectionMode} from '../util/playgrounds/types'; import {IKeyringPair} from '@polkadot/types/types'; describe('EVM collection properties', () => { @@ -163,29 +163,89 @@ }); describe('EVM collection property', () => { - itEth('Set/read properties', async ({helper, privateKey}) => { - const alice = await privateKey('//Alice'); - const collection = await helper.nft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}); + let alice: IKeyringPair; + + before(() => { + usingEthPlaygrounds(async (_helper, privateKey) => { + alice = await privateKey('//Alice'); + }); + }); + + async function testSetReadProperties(helper: EthUniqueHelper, mode: TCollectionMode) { + const collection = await helper[mode].mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}); const sender = await helper.eth.createAccountWithBalance(alice, 100n); await collection.addAdmin(alice, {Ethereum: sender}); const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', sender); + const contract = helper.ethNativeContract.collection(collectionAddress, mode, sender); - const key1 = 'key1'; - const value1 = Buffer.from('value1'); - - const key2 = 'key2'; - const value2 = Buffer.from('value2'); + const keys = ['key0', 'key1']; const writeProperties = [ - [key1, '0x'+value1.toString('hex')], - [key2, '0x'+value2.toString('hex')], + helper.ethProperty.property(keys[0], 'value0'), + helper.ethProperty.property(keys[1], 'value1'), ]; await contract.methods.setCollectionProperties(writeProperties).send(); - const readProperties = await contract.methods.collectionProperties([key1, key2]).call(); + const readProperties = await contract.methods.collectionProperties([keys[0], keys[1]]).call(); expect(readProperties).to.be.like(writeProperties); + } + + itEth('Set/read properties ft', async ({helper}) => { + await testSetReadProperties(helper, 'ft'); + }); + itEth('Set/read properties rft', async ({helper}) => { + await testSetReadProperties(helper, 'rft'); + }); + itEth('Set/read properties nft', async ({helper}) => { + await testSetReadProperties(helper, 'nft'); + }); + + async function testDeleteProperties(helper: EthUniqueHelper, mode: TCollectionMode) { + const collection = await helper[mode].mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const sender = await helper.eth.createAccountWithBalance(alice, 100n); + await collection.addAdmin(alice, {Ethereum: sender}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(collectionAddress, mode, sender); + + const keys = ['key0', 'key1', 'key2', 'key3']; + + { + const writeProperties = [ + helper.ethProperty.property(keys[0], 'value0'), + helper.ethProperty.property(keys[1], 'value1'), + helper.ethProperty.property(keys[2], 'value2'), + helper.ethProperty.property(keys[3], 'value3'), + ]; + + await contract.methods.setCollectionProperties(writeProperties).send(); + const readProperties = await contract.methods.collectionProperties([keys[0], keys[1], keys[2], keys[3]]).call(); + expect(readProperties).to.be.like(writeProperties); + } + + { + const expectProperties = [ + helper.ethProperty.property(keys[0], 'value0'), + helper.ethProperty.property(keys[1], 'value1'), + ]; + + await contract.methods.deleteCollectionProperties([keys[2], keys[3]]).send(); + const readProperties = await contract.methods.collectionProperties([]).call(); + expect(readProperties).to.be.like(expectProperties); + } + } + + itEth('Delete properties ft', async ({helper}) => { + await testDeleteProperties(helper, 'ft'); + }); + itEth('Delete properties rft', async ({helper}) => { + await testDeleteProperties(helper, 'rft'); }); + itEth('Delete properties nft', async ({helper}) => { + await testDeleteProperties(helper, 'nft'); + }); + }); --- a/tests/src/eth/fungibleAbi.json +++ b/tests/src/eth/fungibleAbi.json @@ -293,6 +293,15 @@ "type": "function" }, { + "inputs": [ + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "deleteCollectionProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], "name": "deleteCollectionProperty", "outputs": [], --- a/tests/src/eth/nonFungibleAbi.json +++ b/tests/src/eth/nonFungibleAbi.json @@ -316,6 +316,15 @@ "type": "function" }, { + "inputs": [ + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "deleteCollectionProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], "name": "deleteCollectionProperty", "outputs": [], --- a/tests/src/eth/reFungibleAbi.json +++ b/tests/src/eth/reFungibleAbi.json @@ -298,6 +298,15 @@ "type": "function" }, { + "inputs": [ + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "deleteCollectionProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], "name": "deleteCollectionProperty", "outputs": [], --- a/tests/src/eth/util/playgrounds/types.ts +++ b/tests/src/eth/util/playgrounds/types.ts @@ -19,3 +19,6 @@ readonly field_0: string, readonly field_1: string | Uint8Array, } + +export type EthProperty = string[]; + --- a/tests/src/eth/util/playgrounds/unique.dev.ts +++ b/tests/src/eth/util/playgrounds/unique.dev.ts @@ -18,7 +18,7 @@ import {DevUniqueHelper} from '../../../util/playgrounds/unique.dev'; -import {ContractImports, CompiledContract, TEthCrossAccount, NormalizedEvent} from './types'; +import {ContractImports, CompiledContract, TEthCrossAccount, NormalizedEvent, EthProperty} from './types'; // Native contracts ABI import collectionHelpersAbi from '../../collectionHelpersAbi.json'; @@ -28,6 +28,7 @@ import refungibleTokenAbi from '../../reFungibleTokenAbi.json'; import contractHelpersAbi from './../contractHelpersAbi.json'; import {ICrossAccountId, TEthereumAccount} from '../../../util/playgrounds/types'; +import {TCollectionMode} from '../../../util/playgrounds/types'; class EthGroupBase { helper: EthUniqueHelper; @@ -107,7 +108,7 @@ return new web3.eth.Contract(collectionHelpersAbi as any, '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f', {from: caller, gas: this.helper.eth.DEFAULT_GAS}); } - collection(address: string, mode: 'nft' | 'rft' | 'ft', caller?: string): Contract { + collection(address: string, mode: TCollectionMode, caller?: string): Contract { const abi = { 'nft': nonFungibleAbi, 'rft': refungibleAbi, @@ -336,8 +337,16 @@ normalizeAddress(address: string): string { return '0x' + address.substring(address.length - 40); } -} +} +export class EthPropertyGroup extends EthGroupBase { + property(key: string, value: string): EthProperty { + return [ + key, + '0x'+Buffer.from(value).toString('hex'), + ]; + } +} export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper; export class EthCrossAccountGroup extends EthGroupBase { @@ -369,6 +378,7 @@ ethNativeContract: NativeContractGroup; ethContract: ContractGroup; ethCrossAccount: EthCrossAccountGroup; + ethProperty: EthPropertyGroup; constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { options.helperBase = options.helperBase ?? EthUniqueHelper; @@ -379,6 +389,7 @@ this.ethCrossAccount = new EthCrossAccountGroup(this); this.ethNativeContract = new NativeContractGroup(this); this.ethContract = new ContractGroup(this); + this.ethProperty = new EthPropertyGroup(this); } getWeb3(): Web3 { --- a/tests/src/util/playgrounds/types.ts +++ b/tests/src/util/playgrounds/types.ts @@ -224,3 +224,4 @@ export type TRelayNetworks = 'rococo' | 'westend'; export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks; export type TSigner = IKeyringPair; // | 'string' +export type TCollectionMode = 'nft' | 'rft' | 'ft'; -- gitstuff