git.delta.rocks / unique-network / refs/commits / 7ed3eb8cb302

difftreelog

CORE-337 Rewrite set limits

Trubnikov Sergey2022-05-24parent: #686ae35.patch.diff
in: master

9 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
1616
17use evm_coder::{solidity_interface, types::*, execution::{Result, Error}};17use evm_coder::{solidity_interface, types::*, execution::{Result, Error}};
18pub use pallet_evm::{PrecompileOutput, PrecompileResult, account::CrossAccountId};18pub use pallet_evm::{PrecompileOutput, PrecompileResult, account::CrossAccountId};
19use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder};19use pallet_evm_coder_substrate::dispatch_to_evm;
20use sp_core::{H160, U256};20use sp_core::{H160, U256};
21use sp_std::vec::Vec;21use sp_std::vec::Vec;
22use up_data_structs::Property;22use up_data_structs::{Property, SponsoringRateLimit};
23use alloc::format;
2324
24use crate::{Pallet, CollectionHandle, Config, CollectionProperties};25use crate::{Pallet, CollectionHandle, Config, CollectionProperties};
2526
87 Ok(())88 Ok(())
88 }89 }
8990
90 fn set_limits(91 fn set_limit(
91 &self,92 &mut self,
92 caller: caller,93 caller: caller,
93 limits_json: string,94 limit: string,
95 value: string,
94 ) -> Result<void> {96 ) -> Result<void> {
95 // let mut collection = collection_from_address::<T>(self.contract_address(caller).unwrap(), self.1.gas_left())?;97 check_is_owner(caller, self)?;
96 // check_is_owner(caller, &collection)?;98 let mut limits = self.limits.clone();
9799
98 // let limits = serde_json_core::from_str(limits_json.as_ref())100 match limit.as_str() {
99 // .map_err(|e| Error::Revert(format!("Parse JSON error: {}", e)))?;101 "accountTokenOwnershipLimit" => {
100 // collection.limits = limits.0;102 limits.account_token_ownership_limit = parse_int(value)?;
101 // collection.save().map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;103 },
104 "sponsoredDataSize" => {
105 limits.sponsored_data_size = parse_int(value)?;
106 },
107 "sponsoredDataRateLimit" => {
108 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(parse_int(value)?.unwrap()));
109 },
110 "tokenLimit" => {
111 limits.token_limit = parse_int(value)?;
112 },
113 "sponsorTransferTimeout" => {
114 limits.sponsor_transfer_timeout = parse_int(value)?;
115 },
116 "sponsorApproveTimeout" => {
117 limits.sponsor_approve_timeout = parse_int(value)?;
118 },
119 "ownerCanTransfer" => {
120 limits.owner_can_transfer = parse_bool(value)?;
121 },
122 "ownerCanDestroy" => {
123 limits.owner_can_destroy = parse_bool(value)?;
124 },
125 "transfersEnabled" => {
126 limits.transfers_enabled = parse_bool(value)?;
127 },
128 _ => return Err(Error::Revert(format!("Unknown limit \"{}\"", limit)))
129 }
130 self.limits = limits;
131 save(self);
102 Ok(())132 Ok(())
103 }133 }
104134
107 }137 }
108}138}
109
110fn collection_from_address<T: Config>(
111 collection_address: address,
112 gas_limit: u64
113) -> Result<CollectionHandle<T>> {
114 let collection_id = crate::eth::map_eth_to_id(&collection_address)
115 .ok_or(Error::Revert("Contract is not an unique collection".into()))?;
116 let recorder = <SubstrateRecorder<T>>::new(gas_limit);
117 let collection =
118 CollectionHandle::new_with_recorder(collection_id, recorder)
119 .ok_or(Error::Revert("Create collection handle error".into()))?;
120 Ok(collection)
121}
122139
123fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<()> {140fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<()> {
124 let caller = T::CrossAccountId::from_eth(caller);141 let caller = T::CrossAccountId::from_eth(caller);
132 <crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());149 <crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());
133}150}
151
152fn parse_int(value: string) -> Result<Option<u32>> {
153 value.parse::<u32>()
154 .map_err(|e| Error::Revert(format!("Int value \"{}\" parse error: {}", value, e)))
155 .map(|value| Some(value))
156}
157
158fn parse_bool(value: string) -> Result<Option<bool>> {
159 value.parse::<bool>()
160 .map_err(|e| Error::Revert(format!("Bool value \"{}\" parse error: {}", value, e)))
161 .map(|value| Some(value))
162}
modifiedpallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
51 event MintingFinished();51 event MintingFinished();
52}52}
53
54// Selector: 38e33c60
55contract Collection is Dummy, ERC165 {
56 // Selector: setCollectionProperty(string,bytes) 2f073f66
57 function setCollectionProperty(string memory key, bytes memory value)
58 public
59 {
60 require(false, stub_error);
61 key;
62 value;
63 dummy = 0;
64 }
65
66 // Selector: deleteCollectionProperty(string) 7b7debce
67 function deleteCollectionProperty(string memory key) public {
68 require(false, stub_error);
69 key;
70 dummy = 0;
71 }
72
73 // Throws error if key not found
74 //
75 // Selector: collectionProperty(string) cf24fd6d
76 function collectionProperty(string memory key)
77 public
78 view
79 returns (bytes memory)
80 {
81 require(false, stub_error);
82 key;
83 dummy;
84 return hex"";
85 }
86
87 // Selector: ethSetSponsor(address) 8f9af356
88 function ethSetSponsor(address sponsor) public {
89 require(false, stub_error);
90 sponsor;
91 dummy = 0;
92 }
93
94 // Selector: ethConfirmSponsorship() a8580d1a
95 function ethConfirmSponsorship() public {
96 require(false, stub_error);
97 dummy = 0;
98 }
99
100 // Selector: setLimits(string) 72cb345d
101 function setLimits(string memory limitsJson) public view {
102 require(false, stub_error);
103 limitsJson;
104 dummy;
105 }
106
107 // Selector: contractAddress() f6b4dfb4
108 function contractAddress() public view returns (address) {
109 require(false, stub_error);
110 dummy;
111 return 0x0000000000000000000000000000000000000000;
112 }
113}
11453
115// Selector: 4136937754// Selector: 41369377
116contract TokenProperties is Dummy, ERC165 {55contract TokenProperties is Dummy, ERC165 {
441 }380 }
442}381}
382
383// Selector: f5652829
384contract Collection is Dummy, ERC165 {
385 // Selector: setCollectionProperty(string,bytes) 2f073f66
386 function setCollectionProperty(string memory key, bytes memory value)
387 public
388 {
389 require(false, stub_error);
390 key;
391 value;
392 dummy = 0;
393 }
394
395 // Selector: deleteCollectionProperty(string) 7b7debce
396 function deleteCollectionProperty(string memory key) public {
397 require(false, stub_error);
398 key;
399 dummy = 0;
400 }
401
402 // Throws error if key not found
403 //
404 // Selector: collectionProperty(string) cf24fd6d
405 function collectionProperty(string memory key)
406 public
407 view
408 returns (bytes memory)
409 {
410 require(false, stub_error);
411 key;
412 dummy;
413 return hex"";
414 }
415
416 // Selector: ethSetSponsor(address) 8f9af356
417 function ethSetSponsor(address sponsor) public {
418 require(false, stub_error);
419 sponsor;
420 dummy = 0;
421 }
422
423 // Selector: ethConfirmSponsorship() a8580d1a
424 function ethConfirmSponsorship() public {
425 require(false, stub_error);
426 dummy = 0;
427 }
428
429 // Selector: setLimit(string,string) bf4d2014
430 function setLimit(string memory limit, string memory value) public {
431 require(false, stub_error);
432 limit;
433 value;
434 dummy = 0;
435 }
436
437 // Selector: contractAddress() f6b4dfb4
438 function contractAddress() public view returns (address) {
439 require(false, stub_error);
440 dummy;
441 return 0x0000000000000000000000000000000000000000;
442 }
443}
443444
444contract UniqueNFT is445contract UniqueNFT is
445 Dummy,446 Dummy,
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
26 };26 };
27 use frame_support::traits::Get;27 use frame_support::traits::Get;
28 use sp_core::H160;28 use sp_core::H160;
29 use pallet_common::{CollectionHandle, CollectionById};29 use pallet_common::CollectionById;
30 30
31 use sp_std::vec::Vec;31 use sp_std::vec::Vec;
32 use alloc::format;32 use alloc::format;
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
368#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]368#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
369#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]369#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
370pub struct CollectionLimits {370pub struct CollectionLimits {
371 #[serde(alias = "accountTokenOwnershipLimit")]
372 pub account_token_ownership_limit: Option<u32>,371 pub account_token_ownership_limit: Option<u32>,
373 #[serde(alias = "sponsoredDataSize")]
374 pub sponsored_data_size: Option<u32>,372 pub sponsored_data_size: Option<u32>,
375373
376 /// FIXME should we delete this or repurpose it?374 /// FIXME should we delete this or repurpose it?
377 /// None - setVariableMetadata is not sponsored375 /// None - setVariableMetadata is not sponsored
378 /// Some(v) - setVariableMetadata is sponsored376 /// Some(v) - setVariableMetadata is sponsored
379 /// if there is v block between txs377 /// if there is v block between txs
380 #[serde(alias = "sponsoredDataRateLimit")]
381 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,378 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,
382 #[serde(alias = "tokenLimit")]
383 pub token_limit: Option<u32>,379 pub token_limit: Option<u32>,
384380
385 // Timeouts for item types in passed blocks381 // Timeouts for item types in passed blocks
386 #[serde(alias = "sponsorTransferTimeout")]
387 pub sponsor_transfer_timeout: Option<u32>,382 pub sponsor_transfer_timeout: Option<u32>,
388 #[serde(alias = "sponsorApproveTimeout")]
389 pub sponsor_approve_timeout: Option<u32>,383 pub sponsor_approve_timeout: Option<u32>,
390 #[serde(alias = "ownerCanTransfer")]
391 pub owner_can_transfer: Option<bool>,384 pub owner_can_transfer: Option<bool>,
392 #[serde(alias = "ownerCanDestroy")]
393 pub owner_can_destroy: Option<bool>,385 pub owner_can_destroy: Option<bool>,
394 #[serde(alias = "transfersEnabled")]
395 pub transfers_enabled: Option<bool>,386 pub transfers_enabled: Option<bool>,
396}387}
397388
deletedtests/src/eth/api/Collection.soldiffbeforeafterboth

no changes

modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
42 event MintingFinished();42 event MintingFinished();
43}43}
44
45// Selector: 38e33c60
46interface Collection is Dummy, ERC165 {
47 // Selector: setCollectionProperty(string,bytes) 2f073f66
48 function setCollectionProperty(string memory key, bytes memory value)
49 external;
50
51 // Selector: deleteCollectionProperty(string) 7b7debce
52 function deleteCollectionProperty(string memory key) external;
53
54 // Throws error if key not found
55 //
56 // Selector: collectionProperty(string) cf24fd6d
57 function collectionProperty(string memory key)
58 external
59 view
60 returns (bytes memory);
61
62 // Selector: ethSetSponsor(address) 8f9af356
63 function ethSetSponsor(address sponsor) external;
64
65 // Selector: ethConfirmSponsorship() a8580d1a
66 function ethConfirmSponsorship() external;
67
68 // Selector: setLimits(string) 72cb345d
69 function setLimits(string memory limitsJson) external view;
70
71 // Selector: contractAddress() f6b4dfb4
72 function contractAddress() external view returns (address);
73}
7444
75// Selector: 4136937745// Selector: 41369377
76interface TokenProperties is Dummy, ERC165 {46interface TokenProperties is Dummy, ERC165 {
243 returns (bool);213 returns (bool);
244}214}
215
216// Selector: f5652829
217interface Collection is Dummy, ERC165 {
218 // Selector: setCollectionProperty(string,bytes) 2f073f66
219 function setCollectionProperty(string memory key, bytes memory value)
220 external;
221
222 // Selector: deleteCollectionProperty(string) 7b7debce
223 function deleteCollectionProperty(string memory key) external;
224
225 // Throws error if key not found
226 //
227 // Selector: collectionProperty(string) cf24fd6d
228 function collectionProperty(string memory key)
229 external
230 view
231 returns (bytes memory);
232
233 // Selector: ethSetSponsor(address) 8f9af356
234 function ethSetSponsor(address sponsor) external;
235
236 // Selector: ethConfirmSponsorship() a8580d1a
237 function ethConfirmSponsorship() external;
238
239 // Selector: setLimit(string,string) bf4d2014
240 function setLimit(string memory limit, string memory value) external;
241
242 // Selector: contractAddress() f6b4dfb4
243 function contractAddress() external view returns (address);
244}
245245
246interface UniqueNFT is246interface UniqueNFT is
247 Dummy,247 Dummy,
modifiedtests/src/eth/createCollection.test.tsdiffbeforeafterboth
108 const limits = {108 const limits = {
109 accountTokenOwnershipLimit: 1000,109 accountTokenOwnershipLimit: 1000,
110 sponsoredDataSize: 1024,110 sponsoredDataSize: 1024,
111 sponsoredDataRateLimit: {Blocks: 30},111 sponsoredDataRateLimit: 30,
112 tokenLimit: 1000000,112 tokenLimit: 1000000,
113 sponsorTransferTimeout: 6,113 sponsorTransferTimeout: 6,
114 sponsorApproveTimeout: 6,114 sponsorApproveTimeout: 6,
117 transfersEnabled: false,117 transfersEnabled: false,
118 };118 };
119119
120 const limitsJson = JSON.stringify(limits, null, 1);
121 const collectionEvm = evmCollection(web3, owner, collectionIdAddress);120 const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
122 await collectionEvm.methods.setLimits(limitsJson).send();121 await collectionEvm.methods.setLimit('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit.toString()).send();
123 122 await collectionEvm.methods.setLimit('sponsoredDataSize', limits.sponsoredDataSize.toString()).send();
123 await collectionEvm.methods.setLimit('sponsoredDataRateLimit', limits.sponsoredDataRateLimit.toString()).send();
124 await collectionEvm.methods.setLimit('tokenLimit', limits.tokenLimit.toString()).send();
125 await collectionEvm.methods.setLimit('sponsorTransferTimeout', limits.sponsorTransferTimeout.toString()).send();
126 await collectionEvm.methods.setLimit('sponsorApproveTimeout', limits.sponsorApproveTimeout.toString()).send();
127 await collectionEvm.methods.setLimit('ownerCanTransfer', limits.ownerCanTransfer.toString()).send();
128 await collectionEvm.methods.setLimit('ownerCanDestroy', limits.ownerCanDestroy.toString()).send();
129 await collectionEvm.methods.setLimit('transfersEnabled', limits.transfersEnabled.toString()).send();
130
124 const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;131 const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
125 expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit);132 expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit);
126 expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize);133 expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize);
127 expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit.Blocks);134 expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit);
128 expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit);135 expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit);
129 expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout);136 expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout);
130 expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout);137 expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout);
172 // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);179 // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);
173 });180 });
181
182 itWeb3('Collection address exist', async ({api, web3}) => {
183 const owner = await createEthAccountWithBalance(api, web3);
184 const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
185 const collectionHelper = evmCollectionHelper(web3, owner);
186 expect(await collectionHelper.methods
187 .isCollectionExist(collectionAddressForNonexistentCollection).call())
188 .to.be.false;
189
190 const result = await collectionHelper.methods.create721Collection('Const collection', '5', '5').send();
191 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
192 expect(await collectionHelper.methods
193 .isCollectionExist(collectionIdAddress).call())
194 .to.be.true;
195 });
174});196});
175197
176describe('(!negative tests!) Create collection from EVM', () => {198describe('(!negative tests!) Create collection from EVM', () => {
220 .call()).to.be.rejectedWith('NotSufficientFounds');242 .call()).to.be.rejectedWith('NotSufficientFounds');
221 });243 });
222
223 itWeb3('(!negative test!) Collection address (Create collection handle error)', async ({api, web3}) => {
224 const owner = await createEthAccountWithBalance(api, web3);
225 const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
226 const collectionEvm = evmCollection(web3, owner, collectionAddressForNonexistentCollection);
227 const EXPECTED_ERROR = 'Create collection handle error';
228 {
229 const sponsor = await createEthAccountWithBalance(api, web3);
230 await expect(collectionEvm.methods
231 .setSponsor(sponsor)
232 .call()).to.be.rejectedWith(EXPECTED_ERROR);
233
234 const sponsorCollection = evmCollection(web3, sponsor, collectionAddressForNonexistentCollection);
235 await expect(sponsorCollection.methods
236 .confirmSponsorship()
237 .call()).to.be.rejectedWith(EXPECTED_ERROR);
238 }
239 {
240 const limits = '{"account_token_ownership_limit":1000}';
241 await expect(collectionEvm.methods
242 .setLimits(limits)
243 .call()).to.be.rejectedWith(EXPECTED_ERROR);
244 }
245 });
246244
247 itWeb3('(!negative test!) Check owner', async ({api, web3}) => {245 itWeb3('(!negative test!) Check owner', async ({api, web3}) => {
248 const owner = await createEthAccountWithBalance(api, web3);246 const owner = await createEthAccountWithBalance(api, web3);
255 {253 {
256 const sponsor = await createEthAccountWithBalance(api, web3);254 const sponsor = await createEthAccountWithBalance(api, web3);
257 await expect(contractEvmFromNotOwner.methods255 await expect(contractEvmFromNotOwner.methods
258 .setSponsor(sponsor)256 .ethSetSponsor(sponsor)
259 .call()).to.be.rejectedWith(EXPECTED_ERROR);257 .call()).to.be.rejectedWith(EXPECTED_ERROR);
260 258
261 const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);259 const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
262 await expect(sponsorCollection.methods260 await expect(sponsorCollection.methods
263 .confirmSponsorship()261 .ethConfirmSponsorship()
264 .call()).to.be.rejectedWith('Caller is not set as sponsor');262 .call()).to.be.rejectedWith('Caller is not set as sponsor');
265 }263 }
266 {264 {
267 const limits = '{"account_token_ownership_limit":1000}';
268 await expect(contractEvmFromNotOwner.methods265 await expect(contractEvmFromNotOwner.methods
269 .setLimits(limits)266 .setLimit('account_token_ownership_limit', '1000')
270 .call()).to.be.rejectedWith(EXPECTED_ERROR);267 .call()).to.be.rejectedWith(EXPECTED_ERROR);
271 }268 }
272 });269 });
277 const result = await collectionHelper.methods.create721Collection('Schema collection', 'A', 'A').send();274 const result = await collectionHelper.methods.create721Collection('Schema collection', 'A', 'A').send();
278 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);275 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
279 const collectionEvm = evmCollection(web3, owner, collectionIdAddress);276 const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
277 await expect(collectionEvm.methods
278 .setLimit('badLimit', 'true')
280 const badJson = '{accountTokenOwnershipLimit: 1000}';279 .call()).to.be.rejectedWith('Unknown limit "badLimit"');
281 await expect(collectionEvm.methods280 await expect(collectionEvm.methods
282 .setLimits(badJson)281 .setLimit('sponsoredDataSize', 'badValue')
283 .call()).to.be.rejectedWith('Parse JSON error:');282 .call()).to.be.rejectedWith('Int value "badValue" parse error:');
283 await expect(collectionEvm.methods
284 .setLimit('ownerCanTransfer', 'badValue')
285 .call()).to.be.rejectedWith('Bool value "badValue" parse error:');
284 });286 });
285});287});
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
325 },325 },
326 {326 {
327 "inputs": [327 "inputs": [
328 { "internalType": "string", "name": "limitsJson", "type": "string" }328 { "internalType": "string", "name": "limit", "type": "string" },
329 { "internalType": "string", "name": "value", "type": "string" }
329 ],330 ],
330 "name": "setLimits",331 "name": "setLimit",
331 "outputs": [],332 "outputs": [],
332 "stateMutability": "view",333 "stateMutability": "nonpayable",
333 "type": "function"334 "type": "function"
334 },335 },
335 {336 {