git.delta.rocks / unique-network / refs/commits / 690118c8eab3

difftreelog

add EVM event for `destoyCollection`, refactor `Unique` pallet code, add test for events

PraetorP2022-10-24parent: #73c74cb.patch.diff
in: master

14 files changed

modifiedCargo.lockdiffbeforeafterboth
58255825
5826[[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",
modifiedpallets/common/CHANGELOG.mddiffbeforeafterboth
22
3All notable changes to this project will be documented in this file.3All notable changes to this project will be documented in this file.
44
5## [0.1.9] - 2022-10-13
6
7## Added
8
9- EVM event for `destroy_collection`.
10
5## [0.1.8] - 2022-08-2411## [0.1.8] - 2022-08-24
612
7## Added13## Added
14
8 - Eth methods for collection15- Eth methods for collection
9 + set_collection_sponsor_substrate16 - set_collection_sponsor_substrate
10 + has_collection_pending_sponsor17 - has_collection_pending_sponsor
11 + remove_collection_sponsor18 - remove_collection_sponsor
12 + get_collection_sponsor19 - get_collection_sponsor
13- Add convert function from `uint256` to `CrossAccountId`.20- Add convert function from `uint256` to `CrossAccountId`.
1421
15## [0.1.7] - 2022-08-1922## [0.1.7] - 2022-08-19
1623
17### Added24### Added
1825
19 - Add convert funtion from `CrossAccountId` to eth `uint256`.26- Add convert funtion from `CrossAccountId` to eth `uint256`.
2027
21
22## [0.1.6] - 2022-08-1628## [0.1.6] - 2022-08-16
2329
24### Added30### Added
25- New Ethereum API methods: changeOwner, changeOwner(Substrate) and verifyOwnerOrAdmin(Substrate).
2631
32- New Ethereum API methods: changeOwner, changeOwner(Substrate) and verifyOwnerOrAdmin(Substrate).
33
27<!-- bureaucrate goes here -->34<!-- bureaucrate goes here -->
35
28## [v0.1.5] 2022-08-1636## [v0.1.5] 2022-08-16
2937
30### Other changes38### Other changes
45- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b53- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b
4654
47## [0.1.3] - 2022-07-2555## [0.1.3] - 2022-07-25
56
48### Add57### Add
58
49- Some static property keys and values.59- Some static property keys and values.
5060
51## [0.1.2] - 2022-07-2061## [0.1.2] - 2022-07-20
5262
53### Fixed63### Fixed
5464
55- Some methods in `#[solidity_interface]` for `CollectionHandle` had invalid65- Some methods in `#[solidity_interface]` for `CollectionHandle` had invalid
56 mutability modifiers, causing invalid stub/abi generation.66 mutability modifiers, causing invalid stub/abi generation.
5767
58## [0.1.1] - 2022-07-1468## [0.1.1] - 2022-07-14
5969
60### Added70### Added
6171
62 - 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.
6474
modifiedpallets/common/Cargo.tomldiffbeforeafterboth
1[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"
66
modifiedpallets/common/src/erc.rsdiffbeforeafterboth
53 #[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}
5763
58/// Does not always represent a full collection, for RFT it is either64/// Does not always represent a full collection, for RFT it is either
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
10001000
1001 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));1001 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));
1002
1003 <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 }
10041011
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
19use 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;
2323
24use crate::sp_api_hidden_includes_decl_storage::hidden_include::StorageDoubleMap;24use crate::Pallet;
25
25use pallet_common::{26use pallet_common::{
26 CollectionById,27 CollectionById,
40};41};
4142
42use crate::{43use crate::{Config, SelfWeightOf, weights::WeightInfo};
43 Config, SelfWeightOf, weights::WeightInfo, NftTransferBasket, FungibleTransferBasket,
44 ReFungibleTransferBasket, NftApproveBasket, FungibleApproveBasket, RefungibleApproveBasket,
45};
4644
47use sp_std::vec::Vec;45use sp_std::vec::Vec;
302 }300 }
303301
304 #[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);
305
308 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 collection
314 .check_is_internal()
315 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
316
317 T::CollectionDispatch::destroy(caller, collection)
318 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
319
320 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);
323
324 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);
327
328 Ok(())
329 }310 }
330311
331 /// Check if a collection exists312 /// Check if a collection exists
modifiedpallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth
20/// @dev inlined interface20/// @dev inlined interface
21contract 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}
2425
25/// @title Contract, which allows users to operate with collections26/// @title Contract, which allows users to operate with collections
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
363 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)?);
365
365 let collection = <CollectionHandle<T>>::try_get(collection_id)?;366 Self::destroy_collection_internal(sender, collection_id)
366 collection.check_is_internal()?;
367
368 // =========
369
370 T::CollectionDispatch::destroy(sender, collection)?;
371
372 // TODO: basket cleanup should be moved elsewhere
373 // Maybe runtime dispatch.rs should perform it?
374
375 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);
378
379 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);
382
383 Ok(())
384 }367 }
385368
386 /// Add an address to allow list.369 /// Add an address to allow list.
1152 target_collection.save()1135 target_collection.save()
1153 }1136 }
1137
1138 #[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()?;
1145
1146 T::CollectionDispatch::destroy(sender, collection)?;
1147
1148 // TODO: basket cleanup should be moved elsewhere
1149 // Maybe runtime dispatch.rs should perform it?
1150
1151 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);
1154
1155 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);
1158
1159 Ok(())
1160 }
1154}1161}
11551162
modifiedtests/.vscode/settings.jsondiffbeforeafterboth
1{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}
613
modifiedtests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth
15/// @dev inlined interface15/// @dev inlined interface
16interface 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}
1920
20/// @title Contract, which allows users to operate with collections21/// @title Contract, which allows users to operate with collections
modifiedtests/src/eth/collectionHelpersAbi.jsondiffbeforeafterboth
18 "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",
modifiedtests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth
35 const description = 'Some description';35 const description = 'Some description';
36 const prefix = 'token prefix';36 const prefix = 'token prefix';
3737
38 // 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;
54
40 const collection = helper.nft.getCollectionObject(collectionId);55 const collection = helper.nft.getCollectionObject(collectionId);
41 56 const data = (await collection.getData())!;
57
58 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';
5977
60 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);
79
80 expect(events).to.be.deep.equal([
81 {
82 address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',
83 event: 'CollectionCreated',
84 args: {
85 owner: owner,
86 collectionId: collectionAddress,
87 },
88 },
89 ]);
6190
62 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 });
291
292 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);
296
297
298 const result = await collectionHelper.methods
299 .destroyCollection(collectionAddress)
300 .send({from: owner});
301
302 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 ]);
313
314 expect(await collectionHelper.methods
315 .isCollectionExist(collectionAddress)
316 .call()).to.be.false;
317 });
262});318});
263
modifiedtests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth
174 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)});
182182
183 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 }
188193
189 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);
191196
192 const {collectionId, collectionAddress} = await this.createNFTCollection(signer, name, description, tokenPrefix);197 const {collectionId, collectionAddress, events} = await this.createCollecion('createNFTCollection', signer, name, description, tokenPrefix);
193198
194 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();199 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
195200
196 return {collectionId, collectionAddress};201 return {collectionId, collectionAddress, events};
197 }202 }
198203
199 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);
202
203 const result = await collectionHelper.methods.createRFTCollection(name, description, tokenPrefix).send({value: Number(collectionCreationPrice)});205 return this.createCollecion('createRFTCollection', signer, name, description, tokenPrefix);
204
205 const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
206 const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);
207
208 return {collectionId, collectionAddress};
209 }206 }
210207
211 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);
213210
214 const {collectionId, collectionAddress} = await this.createRFTCollection(signer, name, description, tokenPrefix);211 const {collectionId, collectionAddress, events} = await this.createCollecion('createRFTCollection', signer, name, description, tokenPrefix);
215212
216 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();213 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
217214
218 return {collectionId, collectionAddress};215 return {collectionId, collectionAddress, events};
219 }216 }
220217
221 async deployCollectorContract(signer: string): Promise<Contract> {218 async deployCollectorContract(signer: string): Promise<Contract> {