difftreelog
add EVM event for `destoyCollection`, refactor `Unique` pallet code, add test for events
in: master
14 files changed
Cargo.lockdiffbeforeafterboth582558255826[[package]]5826[[package]]5827name = "pallet-common"5827name = "pallet-common"5828version = "0.1.8"5828version = "0.1.9"5829dependencies = [5829dependencies = [5830 "ethereum",5830 "ethereum",5831 "evm-coder",5831 "evm-coder",pallets/common/CHANGELOG.mddiffbeforeafterboth223All notable changes to this project will be documented in this file.3All notable changes to this project will be documented in this file.445## [0.1.9] - 2022-10-1367## Added89- EVM event for `destroy_collection`.105## [0.1.8] - 2022-08-2411## [0.1.8] - 2022-08-246127## Added13## Added148 - Eth methods for collection15- Eth methods for collection9 + set_collection_sponsor_substrate16 - set_collection_sponsor_substrate10 + has_collection_pending_sponsor17 - has_collection_pending_sponsor11 + remove_collection_sponsor18 - remove_collection_sponsor12 + get_collection_sponsor19 - get_collection_sponsor13- Add convert function from `uint256` to `CrossAccountId`.20- Add convert function from `uint256` to `CrossAccountId`.142115## [0.1.7] - 2022-08-1922## [0.1.7] - 2022-08-19162317### Added24### Added182519 - Add convert funtion from `CrossAccountId` to eth `uint256`.26- Add convert funtion from `CrossAccountId` to eth `uint256`.202721 22## [0.1.6] - 2022-08-1628## [0.1.6] - 2022-08-16232924### Added30### Added25- New Ethereum API methods: changeOwner, changeOwner(Substrate) and verifyOwnerOrAdmin(Substrate).263132- New Ethereum API methods: changeOwner, changeOwner(Substrate) and verifyOwnerOrAdmin(Substrate).3327<!-- bureaucrate goes here -->34<!-- bureaucrate goes here -->3528## [v0.1.5] 2022-08-1636## [v0.1.5] 2022-08-16293730### Other changes38### Other changes45- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b53- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b465447## [0.1.3] - 2022-07-2555## [0.1.3] - 2022-07-255648### Add57### Add5849- Some static property keys and values.59- Some static property keys and values.506051## [0.1.2] - 2022-07-2061## [0.1.2] - 2022-07-20526253### Fixed63### Fixed546455- Some methods in `#[solidity_interface]` for `CollectionHandle` had invalid65- Some methods in `#[solidity_interface]` for `CollectionHandle` had invalid56 mutability modifiers, causing invalid stub/abi generation.66 mutability modifiers, causing invalid stub/abi generation.576758## [0.1.1] - 2022-07-1468## [0.1.1] - 2022-07-14596960### Added70### Added617162 - Implementation of RPC method `token_owners` returning 10 owners in no particular order.72- Implementation of RPC method `token_owners` returning 10 owners in no particular order.63 This was an internal request to improve the web interface and support fractionalization event.73 This was an internal request to improve the web interface and support fractionalization event.6474pallets/common/Cargo.tomldiffbeforeafterboth1[package]1[package]2name = "pallet-common"2name = "pallet-common"3version = "0.1.8"3version = "0.1.9"4license = "GPLv3"4license = "GPLv3"5edition = "2021"5edition = "2021"66pallets/common/src/erc.rsdiffbeforeafterboth53 #[indexed]53 #[indexed]54 collection_id: address,54 collection_id: address,55 },55 },56 /// The collection has been destroyed.57 CollectionDestroyed {58 /// Collection ID.59 #[indexed]60 collection_id: address,61 },56}62}576358/// Does not always represent a full collection, for RFT it is either64/// Does not always represent a full collection, for RFT it is eitherpallets/common/src/lib.rsdiffbeforeafterboth100010001001 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));1001 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));10021003 <PalletEvm<T>>::deposit_log(1004 erc::CollectionHelpersEvents::CollectionDestroyed {1005 collection_id: eth::collection_id_to_address(collection.id),1006 }1007 .to_log(T::ContractAddress::get()),1008 );1002 Ok(())1009 Ok(())1003 }1010 }10041011pallets/unique/src/eth/mod.rsdiffbeforeafterboth19use core::marker::PhantomData;19use core::marker::PhantomData;20use ethereum as _;20use ethereum as _;21use evm_coder::{execution::*, generate_stubgen, solidity_interface, solidity, weight, types::*};21use evm_coder::{execution::*, generate_stubgen, solidity_interface, solidity, weight, types::*};22use frame_support::{traits::Get, storage::StorageNMap};22use frame_support::traits::Get;232324use crate::sp_api_hidden_includes_decl_storage::hidden_include::StorageDoubleMap;24use crate::Pallet;2525use pallet_common::{26use pallet_common::{26 CollectionById,27 CollectionById,40};41};414242use crate::{43use crate::{Config, SelfWeightOf, weights::WeightInfo};43 Config, SelfWeightOf, weights::WeightInfo, NftTransferBasket, FungibleTransferBasket,44 ReFungibleTransferBasket, NftApproveBasket, FungibleApproveBasket, RefungibleApproveBasket,45};464447use sp_std::vec::Vec;45use sp_std::vec::Vec;302 }300 }303301304 #[weight(<SelfWeightOf<T>>::destroy_collection())]302 #[weight(<SelfWeightOf<T>>::destroy_collection())]305 #[solidity(rename_selector = "destroyCollection")]306 fn destroy_collection(&mut self, caller: caller, collection_address: address) -> Result<void> {303 fn destroy_collection(&mut self, caller: caller, collection_address: address) -> Result<void> {307 let caller = T::CrossAccountId::from_eth(caller);304 let caller = T::CrossAccountId::from_eth(caller);305308 let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)306 let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)309 .ok_or("Invalid collection address format".into())307 .ok_or("Invalid collection address format")?;310 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;308 <Pallet<T>>::destroy_collection_internal(caller, collection_id)311 let collection = <pallet_common::CollectionHandle<T>>::try_get(collection_id)312 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;309 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)313 collection314 .check_is_internal()315 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;316317 T::CollectionDispatch::destroy(caller, collection)318 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;319320 let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);321 let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);322 let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);323324 let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);325 let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);326 let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);327328 Ok(())329 }310 }330311331 /// Check if a collection exists312 /// Check if a collection existspallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth20/// @dev inlined interface20/// @dev inlined interface21contract CollectionHelpersEvents {21contract CollectionHelpersEvents {22 event CollectionCreated(address indexed owner, address indexed collectionId);22 event CollectionCreated(address indexed owner, address indexed collectionId);23 event CollectionDestroyed(address indexed collectionId);23}24}242525/// @title Contract, which allows users to operate with collections26/// @title Contract, which allows users to operate with collectionspallets/unique/src/lib.rsdiffbeforeafterboth363 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {363 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {364 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);364 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);365365 let collection = <CollectionHandle<T>>::try_get(collection_id)?;366 Self::destroy_collection_internal(sender, collection_id)366 collection.check_is_internal()?;367368 // =========369370 T::CollectionDispatch::destroy(sender, collection)?;371372 // TODO: basket cleanup should be moved elsewhere373 // Maybe runtime dispatch.rs should perform it?374375 let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);376 let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);377 let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);378379 let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);380 let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);381 let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);382383 Ok(())384 }367 }385368386 /// Add an address to allow list.369 /// Add an address to allow list.1152 target_collection.save()1135 target_collection.save()1153 }1136 }11371138 #[inline(always)]1139 pub(crate) fn destroy_collection_internal(1140 sender: T::CrossAccountId,1141 collection_id: CollectionId,1142 ) -> DispatchResult {1143 let collection = <CollectionHandle<T>>::try_get(collection_id)?;1144 collection.check_is_internal()?;11451146 T::CollectionDispatch::destroy(sender, collection)?;11471148 // TODO: basket cleanup should be moved elsewhere1149 // Maybe runtime dispatch.rs should perform it?11501151 let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1152 let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1153 let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);11541155 let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1156 let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1157 let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);11581159 Ok(())1160 }1154}1161}11551162tests/.vscode/settings.jsondiffbeforeafterboth1{1{2 "mocha.enabled": true,2 "mocha.enabled": true,3 "mochaExplorer.files": "**/*.test.ts",3 "mochaExplorer.files": "**/*.test.ts",4 "mochaExplorer.require": "ts-node/register"4 "mochaExplorer.require": "ts-node/register",5 "eslint.format.enable": true,6 "[javascript]": {7 "editor.defaultFormatter": "dbaeumer.vscode-eslint"8 },9 "[typescript]": {10 "editor.defaultFormatter": "dbaeumer.vscode-eslint"11 }5}12}613tests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth15/// @dev inlined interface15/// @dev inlined interface16interface CollectionHelpersEvents {16interface CollectionHelpersEvents {17 event CollectionCreated(address indexed owner, address indexed collectionId);17 event CollectionCreated(address indexed owner, address indexed collectionId);18 event CollectionDestroyed(address indexed collectionId);18}19}192020/// @title Contract, which allows users to operate with collections21/// @title Contract, which allows users to operate with collectionstests/src/eth/collectionHelpersAbi.jsondiffbeforeafterboth18 "name": "CollectionCreated",18 "name": "CollectionCreated",19 "type": "event"19 "type": "event"20 },20 },21 {22 "anonymous": false,23 "inputs": [24 {25 "indexed": true,26 "internalType": "address",27 "name": "collectionId",28 "type": "address"29 }30 ],31 "name": "CollectionDestroyed",32 "type": "event"33 },21 {34 {22 "inputs": [],35 "inputs": [],23 "name": "collectionCreationFee",36 "name": "collectionCreationFee",tests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth35 const description = 'Some description';35 const description = 'Some description';36 const prefix = 'token prefix';36 const prefix = 'token prefix';373738 // todo:playgrounds this might fail when in async environment.39 const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;38 const {collectionId} = await helper.eth.createNFTCollection(owner, name, description, prefix);40 const {collectionId, collectionAddress, events} = await helper.eth.createNFTCollection(owner, name, description, prefix);41 42 expect(events).to.be.deep.equal([43 {44 address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',45 event: 'CollectionCreated',46 args: {47 owner: owner,48 collectionId: collectionAddress,49 },50 },51 ]);52 39 const data = (await helper.rft.getData(collectionId))!;53 const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;5440 const collection = helper.nft.getCollectionObject(collectionId);55 const collection = helper.nft.getCollectionObject(collectionId);41 56 const data = (await collection.getData())!;5758 expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);59 expect(collectionId).to.be.eq(collectionCountAfter);42 expect(data.name).to.be.eq(name);60 expect(data.name).to.be.eq(name);43 expect(data.description).to.be.eq(description);61 expect(data.description).to.be.eq(description);44 expect(data.raw.tokenPrefix).to.be.eq(prefix);62 expect(data.raw.tokenPrefix).to.be.eq(prefix);57 const prefix = 'token prefix';75 const prefix = 'token prefix';58 const baseUri = 'BaseURI';76 const baseUri = 'BaseURI';597760 const {collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri);78 const {collectionId, collectionAddress, events} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri);7980 expect(events).to.be.deep.equal([81 {82 address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',83 event: 'CollectionCreated',84 args: {85 owner: owner,86 collectionId: collectionAddress,87 },88 },89 ]);619062 const collection = helper.nft.getCollectionObject(collectionId);91 const collection = helper.nft.getCollectionObject(collectionId);63 const data = (await collection.getData())!;92 const data = (await collection.getData())!;260 .call()).to.be.rejectedWith('unknown boolean limit "badLimit"');289 .call()).to.be.rejectedWith('unknown boolean limit "badLimit"');261 });290 });291292 itEth('destroyCollection', async ({helper}) => {293 const owner = await helper.eth.createAccountWithBalance(donor);294 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');295 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);296297298 const result = await collectionHelper.methods299 .destroyCollection(collectionAddress)300 .send({from: owner});301302 const events = helper.eth.normalizeEvents(result.events);303 304 expect(events).to.be.deep.equal([305 {306 address: collectionHelper.options.address,307 event: 'CollectionDestroyed',308 args: {309 collectionId: collectionAddress,310 },311 },312 ]);313314 expect(await collectionHelper.methods315 .isCollectionExist(collectionAddress)316 .call()).to.be.false;317 });262});318});263tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth174 return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);174 return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);175 }175 }176176 177 async createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {177 async createCollecion(functionName: string, signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {178 const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();178 const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();179 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);179 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);180180 181 const result = await collectionHelper.methods.createNFTCollection(name, description, tokenPrefix).send({value: Number(collectionCreationPrice)});181 const result = await collectionHelper.methods[functionName](name, description, tokenPrefix).send({value: Number(collectionCreationPrice)});182182183 const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);183 const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);184 const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);184 const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);185185 const events = this.helper.eth.normalizeEvents(result.events);186 186 return {collectionId, collectionAddress};187 return {collectionId, collectionAddress, events};187 }188 }189 190 async createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {191 return this.createCollecion('createNFTCollection', signer, name, description, tokenPrefix);192 }188193189 async createERC721MetadataCompatibleNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{collectionId: number, collectionAddress: string}> {194 async createERC721MetadataCompatibleNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {190 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);195 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);191196192 const {collectionId, collectionAddress} = await this.createNFTCollection(signer, name, description, tokenPrefix);197 const {collectionId, collectionAddress, events} = await this.createCollecion('createNFTCollection', signer, name, description, tokenPrefix);193198194 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();199 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();195200196 return {collectionId, collectionAddress};201 return {collectionId, collectionAddress, events};197 }202 }198203199 async createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {204 async createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {200 const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice();201 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);202203 const result = await collectionHelper.methods.createRFTCollection(name, description, tokenPrefix).send({value: Number(collectionCreationPrice)});205 return this.createCollecion('createRFTCollection', signer, name, description, tokenPrefix);204205 const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);206 const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);207208 return {collectionId, collectionAddress};209 }206 }210207211 async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{collectionId: number, collectionAddress: string}> {208 async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {212 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);209 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);213210214 const {collectionId, collectionAddress} = await this.createRFTCollection(signer, name, description, tokenPrefix);211 const {collectionId, collectionAddress, events} = await this.createCollecion('createRFTCollection', signer, name, description, tokenPrefix);215212216 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();213 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();217214218 return {collectionId, collectionAddress};215 return {collectionId, collectionAddress, events};219 }216 }220217221 async deployCollectorContract(signer: string): Promise<Contract> {218 async deployCollectorContract(signer: string): Promise<Contract> {