difftreelog
CORE-345 Split evm collection to collection and helper
in: master
16 files changed
Makefilediffbeforeafterboth18COLLECTION_STUBS=./pallets/unique/src/eth/stubs/18COLLECTION_STUBS=./pallets/unique/src/eth/stubs/19COLLECTION_ABI=./tests/src/eth/collectionAbi.json19COLLECTION_ABI=./tests/src/eth/collectionAbi.json2021COLLECTION_HELPER_STUBS=$(COLLECTION_STUBS)22COLLECTION_HELPER_ABI=./tests/src/eth/collectionHelperAbi.json202321TESTS_API=./tests/src/eth/api/24TESTS_API=./tests/src/eth/api/222536 PACKAGE=pallet-evm-contract-helpers NAME=eth::contract_helpers_impl OUTPUT=$(CONTRACT_HELPERS_STUBS)/$@ ./.maintain/scripts/generate_sol.sh39 PACKAGE=pallet-evm-contract-helpers NAME=eth::contract_helpers_impl OUTPUT=$(CONTRACT_HELPERS_STUBS)/$@ ./.maintain/scripts/generate_sol.sh374038Collection.sol:41Collection.sol:39 PACKAGE=pallet-unique NAME=eth::pallet_evm_collection::collection_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh42 PACKAGE=pallet-unique NAME=eth::evm_collection::collection_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh40 PACKAGE=pallet-unique NAME=eth::pallet_evm_collection::collection_impl OUTPUT=$(COLLECTION_STUBS)/$@ ./.maintain/scripts/generate_sol.sh43 PACKAGE=pallet-unique NAME=eth::evm_collection::collection_impl OUTPUT=$(COLLECTION_STUBS)/$@ ./.maintain/scripts/generate_sol.sh4445CollectionHelper.sol:46 PACKAGE=pallet-unique NAME=eth::evm_collection::collection_helper_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh47 PACKAGE=pallet-unique NAME=eth::evm_collection::collection_helper_impl OUTPUT=$(COLLECTION_HELPER_STUBS)/$@ ./.maintain/scripts/generate_sol.sh414842UniqueFungible: UniqueFungible.sol49UniqueFungible: UniqueFungible.sol43 INPUT=$(FUNGIBLE_EVM_STUBS)/$< OUTPUT=$(FUNGIBLE_EVM_STUBS)/UniqueFungible.raw ./.maintain/scripts/compile_stub.sh50 INPUT=$(FUNGIBLE_EVM_STUBS)/$< OUTPUT=$(FUNGIBLE_EVM_STUBS)/UniqueFungible.raw ./.maintain/scripts/compile_stub.sh55 INPUT=$(COLLECTION_STUBS)/$< OUTPUT=$(COLLECTION_STUBS)/Collection.raw ./.maintain/scripts/compile_stub.sh62 INPUT=$(COLLECTION_STUBS)/$< OUTPUT=$(COLLECTION_STUBS)/Collection.raw ./.maintain/scripts/compile_stub.sh56 INPUT=$(COLLECTION_STUBS)/$< OUTPUT=$(COLLECTION_ABI) ./.maintain/scripts/generate_abi.sh63 INPUT=$(COLLECTION_STUBS)/$< OUTPUT=$(COLLECTION_ABI) ./.maintain/scripts/generate_abi.sh6465CollectionHelper: CollectionHelper.sol66 INPUT=$(COLLECTION_HELPER_STUBS)/$< OUTPUT=$(COLLECTION_HELPER_STUBS)/CollectionHelper.raw ./.maintain/scripts/compile_stub.sh67 INPUT=$(COLLECTION_HELPER_STUBS)/$< OUTPUT=$(COLLECTION_HELPER_ABI) ./.maintain/scripts/generate_abi.sh576858evm_stubs: UniqueFungible UniqueNFT ContractHelpers Collection69evm_stubs: UniqueFungible UniqueNFT ContractHelpers Collection CollectionHelper597060.PHONY: _bench71.PHONY: _bench61_bench:72_bench:pallets/unique/src/eth/mod.rsdiffbeforeafterboth112 use sp_std::{vec::Vec, rc::Rc};112 use sp_std::{vec::Vec, rc::Rc};113 use alloc::format;113 use alloc::format;114 114 115 // #[pallet::config]116 pub trait Config:115 pub trait Config:117 frame_system::Config116 frame_system::Config118 + pallet_evm_coder_substrate::Config117 + pallet_evm_coder_substrate::Config122 type ContractAddress: Get<H160>;121 type ContractAddress: Get<H160>;123 }122 }124123124 struct EvmCollectionHelper<T: Config>(Rc<SubstrateRecorder<T>>);125 impl<T: Config> WithRecorder<T> for EvmCollectionHelper<T> {126 fn recorder(&self) -> &SubstrateRecorder<T> {127 &self.0128 }129 130 fn into_recorder(self) -> Rc<SubstrateRecorder<T>> {131 self.0132 }133 }134135 #[solidity_interface(name = "CollectionHelper")]136 impl<T: Config> EvmCollectionHelper<T> {137 fn create_721_collection(138 &self,139 caller: caller,140 name: string,141 description: string,142 token_prefix: string,143 ) -> Result<address> {144 let caller = T::CrossAccountId::from_eth(caller);145 let name = name146 .encode_utf16()147 .collect::<Vec<u16>>()148 .try_into()149 .map_err(|_| error_feild_too_long(stringify!(name), MAX_COLLECTION_NAME_LENGTH))?;150 let description = description151 .encode_utf16()152 .collect::<Vec<u16>>()153 .try_into()154 .map_err(|_| {155 error_feild_too_long(stringify!(description), MAX_COLLECTION_DESCRIPTION_LENGTH)156 })?;157 let token_prefix = token_prefix158 .into_bytes()159 .try_into()160 .map_err(|_| error_feild_too_long(stringify!(token_prefix), MAX_TOKEN_PREFIX_LENGTH))?;161 162 let data = CreateCollectionData {163 name,164 description,165 token_prefix,166 ..Default::default()167 };168 169 let collection_id =170 <pallet_nonfungible::Pallet<T>>::init_collection(caller.as_sub().clone(), data)171 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;172 173 let address = pallet_common::eth::collection_id_to_address(collection_id);174 self.0.log_mirrored(EthCollectionEvent::CollectionCreated {175 owner: *caller.as_eth(),176 collection_id: address,177 });178 Ok(address)179 }180 }181 125 struct EvmCollection<T: Config>(Rc<SubstrateRecorder<T>>);182 struct EvmCollection<T: Config>(Rc<SubstrateRecorder<T>>);126 impl<T: Config> WithRecorder<T> for EvmCollection<T> {183 impl<T: Config> WithRecorder<T> for EvmCollection<T> {145 202 146 #[solidity_interface(name = "Collection")]203 #[solidity_interface(name = "Collection")]147 impl<T: Config> EvmCollection<T> {204 impl<T: Config> EvmCollection<T> {148 fn create_721_collection(149 &self,150 caller: caller,151 name: string,152 description: string,153 token_prefix: string,154 ) -> Result<address> {155 let caller = T::CrossAccountId::from_eth(caller);156 let name = name157 .encode_utf16()158 .collect::<Vec<u16>>()159 .try_into()160 .map_err(|_| error_feild_too_long(stringify!(name), MAX_COLLECTION_NAME_LENGTH))?;161 let description = description162 .encode_utf16()163 .collect::<Vec<u16>>()164 .try_into()165 .map_err(|_| {166 error_feild_too_long(stringify!(description), MAX_COLLECTION_DESCRIPTION_LENGTH)167 })?;168 let token_prefix = token_prefix169 .into_bytes()170 .try_into()171 .map_err(|_| error_feild_too_long(stringify!(token_prefix), MAX_TOKEN_PREFIX_LENGTH))?;172 173 let data = CreateCollectionData {174 name,175 description,176 token_prefix,177 ..Default::default()178 };179 180 let collection_id =181 <pallet_nonfungible::Pallet<T>>::init_collection(caller.as_sub().clone(), data)182 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;183 184 let address = pallet_common::eth::collection_id_to_address(collection_id);185 self.0.log_mirrored(EthCollectionEvent::CollectionCreated {186 owner: *caller.as_eth(),187 collection_id: address,188 });189 Ok(address)190 }191 192 fn set_sponsor(205 fn set_sponsor(193 &self,206 &self,194 caller: caller,207 caller: caller,195 collection_address: address,196 sponsor: address,208 sponsor: address,197 ) -> Result<void> {209 ) -> Result<void> {198 let mut collection = collection_from_address(collection_address, &self.0)?;210 let mut collection = collection_from_address(self.contract_address(caller).unwrap(), &self.0)?;199 check_is_owner(caller, &collection)?;211 check_is_owner(caller, &collection)?;200 212 201 let sponsor = T::CrossAccountId::from_eth(sponsor);213 let sponsor = T::CrossAccountId::from_eth(sponsor);202 collection.set_sponsor(sponsor.as_sub().clone());214 collection.set_sponsor(sponsor.as_sub().clone());203 save_eth(collection)215 save_eth(collection)204 }216 }205 217 206 fn confirm_sponsorship(&self, caller: caller, collection_address: address) -> Result<void> {218 fn confirm_sponsorship(&self, caller: caller) -> Result<void> {207 let mut collection = collection_from_address(collection_address, &self.0)?;219 let mut collection = collection_from_address(self.contract_address(caller).unwrap(), &self.0)?;208 let caller = T::CrossAccountId::from_eth(caller);220 let caller = T::CrossAccountId::from_eth(caller);209 if !collection.confirm_sponsorship(caller.as_sub()) {221 if !collection.confirm_sponsorship(caller.as_sub()) {210 return Err(Error::Revert("Caller is not set as sponsor".into()));222 return Err(Error::Revert("Caller is not set as sponsor".into()));215 fn set_limits(227 fn set_limits(216 &self,228 &self,217 caller: caller,229 caller: caller,218 collection_address: address,219 limits_json: string,230 limits_json: string,220 ) -> Result<void> {231 ) -> Result<void> {221 let mut collection = collection_from_address(collection_address, &self.0)?;232 let mut collection = collection_from_address(self.contract_address(caller).unwrap(), &self.0)?;222 check_is_owner(caller, &collection)?;233 check_is_owner(caller, &collection)?;223 234 224 let limits = serde_json_core::from_str(limits_json.as_ref())235 let limits = serde_json_core::from_str(limits_json.as_ref())227 save_eth(collection)238 save_eth(collection)228 }239 }240241 fn contract_address(&self, _caller: caller) -> Result<address> {242 Ok(self.0.contract())243 }229 }244 }230 245 231 fn error_feild_too_long(feild: &str, bound: u32) -> Error {246 fn error_feild_too_long(feild: &str, bound: u32) -> Error {252 Ok(())267 Ok(())253 }268 }254 269270 pub struct CollectionHelperOnMethodCall<T: Config>(PhantomData<*const T>);271 impl<T: Config> OnMethodCall<T> for CollectionHelperOnMethodCall<T> {272 fn is_reserved(contract: &sp_core::H160) -> bool {273 contract == &T::ContractAddress::get()274 }275 276 fn is_used(contract: &sp_core::H160) -> bool {277 contract == &T::ContractAddress::get()278 }279 280 fn call(281 source: &sp_core::H160,282 target: &sp_core::H160,283 gas_left: u64,284 input: &[u8],285 value: sp_core::U256,286 ) -> Option<PrecompileResult> {287 if target != &T::ContractAddress::get() {288 return None;289 }290 291 let helpers = EvmCollectionHelper::<T>(Rc::new(SubstrateRecorder::<T>::new(*target, gas_left)));292 pallet_evm_coder_substrate::call(*source, helpers, value, input)293 }294 295 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {296 (contract == &T::ContractAddress::get())297 .then(|| include_bytes!("./stubs/CollectionHelper.raw").to_vec())298 }299 }300 301 generate_stubgen!(collection_helper_impl, CollectionHelperCall<()>, true);302 generate_stubgen!(collection_helper_iface, CollectionHelperCall<()>, false);303 255 pub struct CollectionOnMethodCall<T: Config>(PhantomData<*const T>);304 pub struct CollectionOnMethodCall<T: Config>(PhantomData<*const T>);256 impl<T: Config> OnMethodCall<T> for CollectionOnMethodCall<T> {305 impl<T: Config> OnMethodCall<T> for CollectionOnMethodCall<T> {269 input: &[u8],318 input: &[u8],270 value: sp_core::U256,319 value: sp_core::U256,271 ) -> Option<PrecompileResult> {320 ) -> Option<PrecompileResult> {272 if target != &T::ContractAddress::get() {273 return None;274 }275 276 let helpers = EvmCollection::<T>(Rc::new(SubstrateRecorder::<T>::new(*target, gas_left)));321 let helpers = EvmCollection::<T>(Rc::new(SubstrateRecorder::<T>::new(*target, gas_left)));277 pallet_evm_coder_substrate::call(*source, helpers, value, input)322 pallet_evm_coder_substrate::call(*source, helpers, value, input)pallets/unique/src/eth/stubs/Collection.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/Collection.soldiffbeforeafterboth21 }21 }22}22}232324// Selector: 1e95830f24// Selector: 15cc740e25contract Collection is Dummy, ERC165 {25contract Collection is Dummy, ERC165 {26 // Selector: create721Collection(string,string,string) 951c015126 // Selector: setSponsor(address) 59753fb127 function create721Collection(27 function setSponsor(address sponsor) public view {28 string memory name,29 string memory description,30 string memory tokenPrefix31 ) public view returns (address) {32 require(false, stub_error);28 require(false, stub_error);33 name;29 sponsor;34 description;35 tokenPrefix;36 dummy;30 dummy;37 return 0x0000000000000000000000000000000000000000;38 }31 }393240 // Selector: setSponsor(address,address) f01fba9333 // Selector: confirmSponsorship() c8c6a05641 function setSponsor(address collectionAddress, address sponsor)34 function confirmSponsorship() public view {42 public43 view44 {45 require(false, stub_error);35 require(false, stub_error);46 collectionAddress;47 sponsor;48 dummy;36 dummy;49 }37 }503851 // Selector: confirmSponsorship(address) abc0000139 // Selector: setLimits(string) 72cb345d52 function confirmSponsorship(address collectionAddress) public view {40 function setLimits(string memory limitsJson) public view {53 require(false, stub_error);41 require(false, stub_error);54 collectionAddress;42 limitsJson;55 dummy;43 dummy;56 }44 }574558 // Selector: setLimits(address,string) d05638cc46 // Selector: contractAddress() f6b4dfb459 function setLimits(address collectionAddress, string memory limitsJson)47 function contractAddress() public view returns (address) {60 public61 view62 {63 require(false, stub_error);48 require(false, stub_error);64 collectionAddress;65 limitsJson;66 dummy;49 dummy;50 return 0x0000000000000000000000000000000000000000;67 }51 }68}52}6953pallets/unique/src/eth/stubs/CollectionHelper.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/CollectionHelper.soldiffbeforeafterbothno changes
runtime/opal/src/lib.rsdiffbeforeafterboth308 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,308 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,309 CollectionDispatchT<Self>,309 CollectionDispatchT<Self>,310 evm_collection::CollectionOnMethodCall<Self>,310 evm_collection::CollectionOnMethodCall<Self>,311 evm_collection::CollectionHelperOnMethodCall<Self>,311 );312 );312 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;313 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;313 type ChainId = ChainId;314 type ChainId = ChainId;978 ]);979 ]);979980980 // 0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f981 // 0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f981 pub const EvmCollectionAddress: H160 = H160([982 pub const EvmCollectionHelperAddress: H160 = H160([982 0x6c, 0x4e, 0x9f, 0xe1, 0xae, 0x37, 0xa4, 0x1e, 0x93, 0xce, 0xe4, 0x29, 0xe8, 0xe1, 0x88, 0x1a, 0xbd, 0xcb, 0xb5, 0x4f,983 0x6c, 0x4e, 0x9f, 0xe1, 0xae, 0x37, 0xa4, 0x1e, 0x93, 0xce, 0xe4, 0x29, 0xe8, 0xe1, 0x88, 0x1a, 0xbd, 0xcb, 0xb5, 0x4f,983 ]);984 ]);984}985}989}990}990991991impl evm_collection::Config for Runtime {992impl evm_collection::Config for Runtime {992 type ContractAddress = EvmCollectionAddress;993 type ContractAddress = EvmCollectionHelperAddress;993}994}994995995construct_runtime!(996construct_runtime!(runtime/quartz/src/lib.rsdiffbeforeafterboth280 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,280 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,281 CollectionDispatchT<Self>,281 CollectionDispatchT<Self>,282 evm_collection::CollectionOnMethodCall<Self>,282 evm_collection::CollectionOnMethodCall<Self>,283 evm_collection::CollectionHelperOnMethodCall<Self>,283 );284 );284 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;285 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;285 type ChainId = ChainId;286 type ChainId = ChainId;955 ]);956 ]);956 957 957 // 0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f958 // 0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f958 pub const EvmCollectionAddress: H160 = H160([959 pub const EvmCollectionHelperAddress: H160 = H160([959 0x6c, 0x4e, 0x9f, 0xe1, 0xae, 0x37, 0xa4, 0x1e, 0x93, 0xce, 0xe4, 0x29, 0xe8, 0xe1, 0x88, 0x1a, 0xbd, 0xcb, 0xb5, 0x4f,960 0x6c, 0x4e, 0x9f, 0xe1, 0xae, 0x37, 0xa4, 0x1e, 0x93, 0xce, 0xe4, 0x29, 0xe8, 0xe1, 0x88, 0x1a, 0xbd, 0xcb, 0xb5, 0x4f,960 ]);961 ]);961}962}966}967}967968968impl evm_collection::Config for Runtime {969impl evm_collection::Config for Runtime {969 type ContractAddress = EvmCollectionAddress;970 type ContractAddress = EvmCollectionHelperAddress;970}971}971972972construct_runtime!(973construct_runtime!(runtime/unique/src/lib.rsdiffbeforeafterboth284 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,284 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,285 CollectionDispatchT<Self>,285 CollectionDispatchT<Self>,286 evm_collection::CollectionOnMethodCall<Self>,286 evm_collection::CollectionOnMethodCall<Self>,287 evm_collection::CollectionHelperOnMethodCall<Self>,287 );288 );288 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;289 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;289 type ChainId = ChainId;290 type ChainId = ChainId;960 ]);961 ]);961 962 962 // 0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f963 // 0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f963 pub const EvmCollectionAddress: H160 = H160([964 pub const EvmCollectionHelperAddress: H160 = H160([964 0x6c, 0x4e, 0x9f, 0xe1, 0xae, 0x37, 0xa4, 0x1e, 0x93, 0xce, 0xe4, 0x29, 0xe8, 0xe1, 0x88, 0x1a, 0xbd, 0xcb, 0xb5, 0x4f,965 0x6c, 0x4e, 0x9f, 0xe1, 0xae, 0x37, 0xa4, 0x1e, 0x93, 0xce, 0xe4, 0x29, 0xe8, 0xe1, 0x88, 0x1a, 0xbd, 0xcb, 0xb5, 0x4f,965 ]);966 ]);966}967}971}972}972973973impl evm_collection::Config for Runtime {974impl evm_collection::Config for Runtime {974 type ContractAddress = EvmCollectionAddress;975 type ContractAddress = EvmCollectionHelperAddress;975}976}976977977construct_runtime!(978construct_runtime!(tests/src/eth/api/Collection.soldiffbeforeafterboth12 function supportsInterface(bytes4 interfaceID) external view returns (bool);12 function supportsInterface(bytes4 interfaceID) external view returns (bool);13}13}141415// Selector: 1e95830f15// Selector: 15cc740e16interface Collection is Dummy, ERC165 {16interface Collection is Dummy, ERC165 {17 // Selector: create721Collection(string,string,string) 951c015118 function create721Collection(19 string memory name,20 string memory description,21 string memory tokenPrefix22 ) external view returns (address);2324 // Selector: setSponsor(address,address) f01fba9317 // Selector: setSponsor(address) 59753fb125 function setSponsor(address collectionAddress, address sponsor)18 function setSponsor(address sponsor) external view;26 external27 view;281929 // Selector: confirmSponsorship(address) abc0000120 // Selector: confirmSponsorship() c8c6a05630 function confirmSponsorship(address collectionAddress) external view;21 function confirmSponsorship() external view;312232 // Selector: setLimits(address,string) d05638cc23 // Selector: setLimits(string) 72cb345d33 function setLimits(address collectionAddress, string memory limitsJson)24 function setLimits(string memory limitsJson) external view;34 external2535 view;26 // Selector: contractAddress() f6b4dfb427 function contractAddress() external view returns (address);36}28}3729tests/src/eth/api/CollectionHelper.soldiffbeforeafterbothno changes
tests/src/eth/collectionAbi.jsondiffbeforeafterboth1[1[2 {2 {3 "inputs": [3 "inputs": [],4 {5 "internalType": "address",6 "name": "collectionAddress",7 "type": "address"8 }9 ],10 "name": "confirmSponsorship",4 "name": "confirmSponsorship",11 "outputs": [],5 "outputs": [],14 },8 },15 {9 {16 "inputs": [10 "inputs": [],17 { "internalType": "string", "name": "name", "type": "string" },18 { "internalType": "string", "name": "description", "type": "string" },19 { "internalType": "string", "name": "tokenPrefix", "type": "string" }20 ],21 "name": "create721Collection",11 "name": "contractAddress",22 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],12 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],23 "stateMutability": "view",13 "stateMutability": "view",24 "type": "function"14 "type": "function"25 },15 },26 {16 {27 "inputs": [17 "inputs": [28 {29 "internalType": "address",30 "name": "collectionAddress",31 "type": "address"32 },33 { "internalType": "string", "name": "limitsJson", "type": "string" }18 { "internalType": "string", "name": "limitsJson", "type": "string" }34 ],19 ],35 "name": "setLimits",20 "name": "setLimits",39 },24 },40 {25 {41 "inputs": [26 "inputs": [42 {43 "internalType": "address",44 "name": "collectionAddress",45 "type": "address"46 },47 { "internalType": "address", "name": "sponsor", "type": "address" }27 { "internalType": "address", "name": "sponsor", "type": "address" }48 ],28 ],49 "name": "setSponsor",29 "name": "setSponsor",tests/src/eth/collectionHelperAbi.jsondiffbeforeafterbothno changes
tests/src/eth/createCollection.test.tsdiffbeforeafterboth20import {expect} from 'chai';20import {expect} from 'chai';21import {getCreatedCollectionCount, getDetailedCollectionInfo} from '../util/helpers';21import {getCreatedCollectionCount, getDetailedCollectionInfo} from '../util/helpers';22import {22import {23 collectionHelper,23 evmCollectionHelper,24 collectionIdFromAddress,24 collectionIdFromAddress,25 collectionIdToAddress,25 collectionIdToAddress,26 createEthAccount,26 createEthAccount,27 createEthAccountWithBalance,27 createEthAccountWithBalance,28 evmCollection,28 GAS_ARGS,29 GAS_ARGS,29 itWeb3,30 itWeb3,30 normalizeAddress,31 normalizeAddress,41describe('Create collection from EVM', () => {42describe('Create collection from EVM', () => {42 itWeb3('Create collection', async ({api, web3}) => {43 itWeb3('Create collection', async ({api, web3}) => {43 const owner = await createEthAccountWithBalance(api, web3);44 const owner = await createEthAccountWithBalance(api, web3);44 const helper = collectionHelper(web3, owner);45 const helper = evmCollectionHelper(web3, owner);45 const collectionName = 'CollectionEVM';46 const collectionName = 'CollectionEVM';46 const description = 'Some description';47 const description = 'Some description';47 const tokenPrefix = 'token prefix';48 const tokenPrefix = 'token prefix';63 64 64 itWeb3('Set sponsorship', async ({api, web3}) => {65 itWeb3('Set sponsorship', async ({api, web3}) => {65 const owner = await createEthAccountWithBalance(api, web3);66 const owner = await createEthAccountWithBalance(api, web3);66 const helper = collectionHelper(web3, owner);67 const collectionHelper = evmCollectionHelper(web3, owner);67 let result = await helper.methods.create721Collection('Sponsor collection', '1', '1').send();68 let result = await collectionHelper.methods.create721Collection('Sponsor collection', '1', '1').send();68 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);69 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);69 const sponsor = await createEthAccountWithBalance(api, web3);70 const sponsor = await createEthAccountWithBalance(api, web3);71 const collectionEvm = evmCollection(web3, owner, collectionIdAddress);70 result = await helper.methods.setSponsor(collectionIdAddress, sponsor).send();72 result = await collectionEvm.methods.setSponsor(sponsor).send();71 let collection = (await getDetailedCollectionInfo(api, collectionId))!;73 let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;72 expect(collection.sponsorship.isUnconfirmed).to.be.true;74 expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;73 expect(collection.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));75 expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));74 await expect(helper.methods.confirmSponsorship(collectionIdAddress).call()).to.be.rejectedWith('Caller is not set as sponsor');76 await expect(collectionEvm.methods.confirmSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');75 const sponsorHelper = collectionHelper(web3, sponsor);77 const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);76 await sponsorHelper.methods.confirmSponsorship(collectionIdAddress).send();78 await sponsorCollection.methods.confirmSponsorship().send();77 collection = (await getDetailedCollectionInfo(api, collectionId))!;79 collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;78 expect(collection.sponsorship.isConfirmed).to.be.true;80 expect(collectionSub.sponsorship.isConfirmed).to.be.true;79 expect(collection.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));81 expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));80 });82 });818382 itWeb3('Set limits', async ({api, web3}) => {84 itWeb3('Set limits', async ({api, web3}) => {83 const owner = await createEthAccountWithBalance(api, web3);85 const owner = await createEthAccountWithBalance(api, web3);84 const helper = collectionHelper(web3, owner);86 const collectionHelper = evmCollectionHelper(web3, owner);85 const result = await helper.methods.create721Collection('Const collection', '5', '5').send();87 const result = await collectionHelper.methods.create721Collection('Const collection', '5', '5').send();86 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);88 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);87 const limits = {89 const limits = {88 accountTokenOwnershipLimit: 1000,90 accountTokenOwnershipLimit: 1000,97 };99 };9810099 const limitsJson = JSON.stringify(limits, null, 1);101 const limitsJson = JSON.stringify(limits, null, 1);102 const collectionEvm = evmCollection(web3, owner, collectionIdAddress);100 await helper.methods.setLimits(collectionIdAddress, limitsJson).send();103 await collectionEvm.methods.setLimits(limitsJson).send();101 104 102 const collection = (await getDetailedCollectionInfo(api, collectionId))!;105 const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;103 expect(collection.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit);106 expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit);104 expect(collection.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize);107 expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize);105 expect(collection.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit.Blocks);108 expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit.Blocks);106 expect(collection.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit);109 expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit);107 expect(collection.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout);110 expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout);108 expect(collection.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout);111 expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout);109 expect(collection.limits.ownerCanTransfer.toHuman()).to.be.eq(limits.ownerCanTransfer);112 expect(collectionSub.limits.ownerCanTransfer.toHuman()).to.be.eq(limits.ownerCanTransfer);110 expect(collection.limits.ownerCanDestroy.toHuman()).to.be.eq(limits.ownerCanDestroy);113 expect(collectionSub.limits.ownerCanDestroy.toHuman()).to.be.eq(limits.ownerCanDestroy);111 expect(collection.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled);114 expect(collectionSub.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled);112 });115 });113116114 itWeb3('Check tokenURI', async ({web3, api}) => {117 itWeb3('Check tokenURI', async ({web3, api}) => {115 const owner = await createEthAccountWithBalance(api, web3);118 const owner = await createEthAccountWithBalance(api, web3);116 const helper = collectionHelper(web3, owner);119 const helper = evmCollectionHelper(web3, owner);117 let result = await helper.methods.create721Collection('Mint collection', '6', '6').send();120 let result = await helper.methods.create721Collection('Mint collection', '6', '6').send();118 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);121 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);119 const receiver = createEthAccount(web3);122 const receiver = createEthAccount(web3);154describe('(!negative tests!) Create collection from EVM', () => {157describe('(!negative tests!) Create collection from EVM', () => {155 itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3}) => {158 itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3}) => {156 const owner = await createEthAccountWithBalance(api, web3);159 const owner = await createEthAccountWithBalance(api, web3);157 const helper = collectionHelper(web3, owner);160 const helper = evmCollectionHelper(web3, owner);158 {161 {159 const MAX_NAME_LENGHT = 64;162 const MAX_NAME_LENGHT = 64;160 const collectionName = 'A'.repeat(MAX_NAME_LENGHT + 1);163 const collectionName = 'A'.repeat(MAX_NAME_LENGHT + 1);188 191 189 itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => {192 itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => {190 const owner = await createEthAccount(web3);193 const owner = await createEthAccount(web3);191 const helper = collectionHelper(web3, owner);194 const helper = evmCollectionHelper(web3, owner);192 const collectionName = 'A';195 const collectionName = 'A';193 const description = 'A';196 const description = 'A';194 const tokenPrefix = 'A';197 const tokenPrefix = 'A';200203201 itWeb3('(!negative test!) Collection address (Contract is not an unique collection)', async ({api, web3}) => {204 itWeb3('(!negative test!) Collection address (Contract is not an unique collection)', async ({api, web3}) => {202 const owner = await createEthAccountWithBalance(api, web3);205 const owner = await createEthAccountWithBalance(api, web3);206 const collectionAddressWithBadPrefix = '0x00112233445566778899AABBCCDDEEFF00112233';203 const helper = collectionHelper(web3, owner);207 const collectionEvm = evmCollection(web3, owner, collectionAddressWithBadPrefix);204 const collectionAddressWithBadPrefix = '0x00112233445566778899AABBCCDDEEFF00112233';205 const EXPECTED_ERROR = 'Contract is not an unique collection';208 const EXPECTED_ERROR = 'Contract is not an unique collection';206 {209 {207 const sponsor = await createEthAccountWithBalance(api, web3);210 const sponsor = await createEthAccountWithBalance(api, web3);208 await expect(helper.methods211 await expect(collectionEvm.methods209 .setSponsor(collectionAddressWithBadPrefix, sponsor)212 .setSponsor(sponsor)210 .call()).to.be.rejectedWith(EXPECTED_ERROR);213 .call()).to.be.rejectedWith(EXPECTED_ERROR);211 214 212 const sponsorHelper = collectionHelper(web3, sponsor);215 const sponsorCollection = evmCollection(web3, sponsor, collectionAddressWithBadPrefix);213 await expect(sponsorHelper.methods216 await expect(sponsorCollection.methods214 .confirmSponsorship(collectionAddressWithBadPrefix)217 .confirmSponsorship()215 .call()).to.be.rejectedWith(EXPECTED_ERROR);218 .call()).to.be.rejectedWith(EXPECTED_ERROR);216 }219 }217 {220 {218 const limits = '{"account_token_ownership_limit":1000}';221 const limits = '{"account_token_ownership_limit":1000}';219 await expect(helper.methods222 await expect(collectionEvm.methods220 .setLimits(collectionAddressWithBadPrefix, limits)223 .setLimits(limits)221 .call()).to.be.rejectedWith(EXPECTED_ERROR);224 .call()).to.be.rejectedWith(EXPECTED_ERROR);222 }225 }223 });226 });224227225 itWeb3('(!negative test!) Check owner', async ({api, web3}) => {228 itWeb3('(!negative test!) Check owner', async ({api, web3}) => {226 const owner = await createEthAccountWithBalance(api, web3);229 const owner = await createEthAccountWithBalance(api, web3);227 const notOwner = await createEthAccount(web3);230 const notOwner = await createEthAccount(web3);228 const helperFromOwner = collectionHelper(web3, owner);231 const collectionHelper = evmCollectionHelper(web3, owner);229 const helperFromNotOwner = collectionHelper(web3, notOwner);230 const result = await helperFromOwner.methods.create721Collection('A', 'A', 'A').send();232 const result = await collectionHelper.methods.create721Collection('A', 'A', 'A').send();231 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);233 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);234 const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress);232 const EXPECTED_ERROR = 'NoPermission';235 const EXPECTED_ERROR = 'NoPermission';233 {236 {234 const sponsor = await createEthAccountWithBalance(api, web3);237 const sponsor = await createEthAccountWithBalance(api, web3);235 await expect(helperFromNotOwner.methods238 await expect(contractEvmFromNotOwner.methods236 .setSponsor(collectionIdAddress, sponsor)239 .setSponsor(sponsor)237 .call()).to.be.rejectedWith(EXPECTED_ERROR);240 .call()).to.be.rejectedWith(EXPECTED_ERROR);238 241 239 const sponsorHelper = collectionHelper(web3, sponsor);242 const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);240 await expect(sponsorHelper.methods243 await expect(sponsorCollection.methods241 .confirmSponsorship(collectionIdAddress)244 .confirmSponsorship()242 .call()).to.be.rejectedWith('Caller is not set as sponsor');245 .call()).to.be.rejectedWith('Caller is not set as sponsor');243 }246 }244 {247 {245 const limits = '{"account_token_ownership_limit":1000}';248 const limits = '{"account_token_ownership_limit":1000}';246 await expect(helperFromNotOwner.methods249 await expect(contractEvmFromNotOwner.methods247 .setLimits(collectionIdAddress, limits)250 .setLimits(limits)248 .call()).to.be.rejectedWith(EXPECTED_ERROR);251 .call()).to.be.rejectedWith(EXPECTED_ERROR);249 }252 }250 });253 });251254252 itWeb3('(!negative test!) Set limits', async ({api, web3}) => {255 itWeb3('(!negative test!) Set limits', async ({api, web3}) => {253 const owner = await createEthAccountWithBalance(api, web3);256 const owner = await createEthAccountWithBalance(api, web3);254 const helper = collectionHelper(web3, owner);257 const collectionHelper = evmCollectionHelper(web3, owner);255 const result = await helper.methods.create721Collection('Schema collection', 'A', 'A').send();258 const result = await collectionHelper.methods.create721Collection('Schema collection', 'A', 'A').send();256 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);259 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);260 const collectionEvm = evmCollection(web3, owner, collectionIdAddress);257 const badJson = '{accountTokenOwnershipLimit: 1000}';261 const badJson = '{accountTokenOwnershipLimit: 1000}';258 await expect(helper.methods262 await expect(collectionEvm.methods259 .setLimits(collectionIdAddress, badJson)263 .setLimits(badJson)260 .call()).to.be.rejectedWith('Parse JSON error:');264 .call()).to.be.rejectedWith('Parse JSON error:');261 });265 });262});266});tests/src/eth/util/helpers.tsdiffbeforeafterboth29import privateKey from '../../substrate/privateKey';29import privateKey from '../../substrate/privateKey';30import contractHelpersAbi from './contractHelpersAbi.json';30import contractHelpersAbi from './contractHelpersAbi.json';31import collectionAbi from '../collectionAbi.json';31import collectionAbi from '../collectionAbi.json';32import collectionHelperAbi from '../collectionHelperAbi.json';32import getBalance from '../../substrate/get-balance';33import getBalance from '../../substrate/get-balance';33import waitNewBlocks from '../../substrate/wait-new-blocks';34import waitNewBlocks from '../../substrate/wait-new-blocks';3435282 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});283 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});283}284}284285285/** 286/** 286 * pallet evm_collection287 * evm collection helper287 * @param web3 288 * @param web3 288 * @param caller - eth address289 * @param caller - eth address289 * @returns 290 * @returns 290 */291 */291export function collectionHelper(web3: Web3, caller: string) {292export function evmCollectionHelper(web3: Web3, caller: string) {292 return new web3.eth.Contract(collectionAbi as any, '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f', {from: caller, ...GAS_ARGS});293 return new web3.eth.Contract(collectionHelperAbi as any, '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f', {from: caller, ...GAS_ARGS});293}294}295296/** 297 * evm collection298 * @param web3 299 * @param caller - eth address300 * @returns 301 */302export function evmCollection(web3: Web3, caller: string, collection: string) {303 return new web3.eth.Contract(collectionAbi as any, collection, {from: caller, ...GAS_ARGS});304}294305295/**306/**296 * Execute ethereum method call using substrate account307 * Execute ethereum method call using substrate accounttests/src/interfaces/unique/types.tsdiffbeforeafterboth6import type { AccountId32, Call, H160, H256, MultiAddress, Perbill, Permill } from '@polkadot/types/interfaces/runtime';6import type { AccountId32, Call, H160, H256, MultiAddress, Perbill, Permill } from '@polkadot/types/interfaces/runtime';7import type { Event } from '@polkadot/types/interfaces/system';7import type { Event } from '@polkadot/types/interfaces/system';89/** @name BTreeSet */10export interface BTreeSet extends BTreeSet<Bytes> {}11812/** @name CumulusPalletDmpQueueCall */9/** @name CumulusPalletDmpQueueCall */13export interface CumulusPalletDmpQueueCall extends Enum {10export interface CumulusPalletDmpQueueCall extends Enum {