--- a/Cargo.lock +++ b/Cargo.lock @@ -5504,6 +5504,7 @@ "pallet-ethereum", "pallet-evm", "pallet-evm-coder-substrate", + "pallet-evm-collection", "pallet-evm-contract-helpers", "pallet-evm-migration", "pallet-evm-transaction-payment", @@ -6075,6 +6076,28 @@ ] [[package]] +name = "pallet-evm-collection" +version = "0.1.0" +dependencies = [ + "ethereum", + "evm-coder", + "fp-evm-mapping", + "frame-support", + "frame-system", + "log", + "pallet-common", + "pallet-evm", + "pallet-evm-coder-substrate", + "pallet-nonfungible", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", + "up-data-structs", +] + +[[package]] name = "pallet-evm-contract-helpers" version = "0.1.0" dependencies = [ @@ -8784,6 +8807,7 @@ "pallet-ethereum", "pallet-evm", "pallet-evm-coder-substrate", + "pallet-evm-collection", "pallet-evm-contract-helpers", "pallet-evm-migration", "pallet-evm-transaction-payment", @@ -12845,6 +12869,7 @@ "pallet-ethereum", "pallet-evm", "pallet-evm-coder-substrate", + "pallet-evm-collection", "pallet-evm-contract-helpers", "pallet-evm-migration", "pallet-evm-transaction-payment", --- a/Makefile +++ b/Makefile @@ -15,6 +15,9 @@ CONTRACT_HELPERS_STUBS=./pallets/evm-contract-helpers/src/stubs/ CONTRACT_HELPERS_ABI=./tests/src/eth/util/contractHelpersAbi.json +COLLECTION_STUBS=./pallets/evm-collection/src/stubs/ +COLLECTION_ABI=./tests/src/eth/collectionAbi.json + TESTS_API=./tests/src/eth/api/ .PHONY: regenerate_solidity @@ -32,6 +35,10 @@ PACKAGE=pallet-evm-contract-helpers NAME=eth::contract_helpers_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh PACKAGE=pallet-evm-contract-helpers NAME=eth::contract_helpers_impl OUTPUT=$(CONTRACT_HELPERS_STUBS)/$@ ./.maintain/scripts/generate_sol.sh +Collection.sol: + PACKAGE=pallet-evm-collection NAME=eth::contract_helpers_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh + PACKAGE=pallet-evm-collection NAME=eth::contract_helpers_impl OUTPUT=$(COLLECTION_STUBS)/$@ ./.maintain/scripts/generate_sol.sh + UniqueFungible: UniqueFungible.sol INPUT=$(FUNGIBLE_EVM_STUBS)/$< OUTPUT=$(FUNGIBLE_EVM_STUBS)/UniqueFungible.raw ./.maintain/scripts/compile_stub.sh INPUT=$(FUNGIBLE_EVM_STUBS)/$< OUTPUT=$(FUNGIBLE_EVM_ABI) ./.maintain/scripts/generate_abi.sh @@ -44,7 +51,11 @@ INPUT=$(CONTRACT_HELPERS_STUBS)/$< OUTPUT=$(CONTRACT_HELPERS_STUBS)/ContractHelpers.raw ./.maintain/scripts/compile_stub.sh INPUT=$(CONTRACT_HELPERS_STUBS)/$< OUTPUT=$(CONTRACT_HELPERS_ABI) ./.maintain/scripts/generate_abi.sh -evm_stubs: UniqueFungible UniqueNFT ContractHelpers +Collection: Collection.sol + INPUT=$(COLLECTION_STUBS)/$< OUTPUT=$(COLLECTION_STUBS)/Collection.raw ./.maintain/scripts/compile_stub.sh + INPUT=$(COLLECTION_STUBS)/$< OUTPUT=$(COLLECTION_ABI) ./.maintain/scripts/generate_abi.sh + +evm_stubs: UniqueFungible UniqueNFT ContractHelpers Collection .PHONY: _bench _bench: --- /dev/null +++ b/pallets/evm-collection/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "pallet-evm-collection" +version = "0.1.0" +license = "GPLv3" +edition = "2021" + +[dependencies] +scale-info = { version = "2.0.1", default-features = false, features = [ + "derive", +] } +ethereum = { version = "0.12.0", default-features = false } +log = { default-features = false, version = "0.4.14" } + +# Substrate +frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.21' } +frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.21' } +sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.21' } +sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.21' } +sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.21' } + +# Unique +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.21-logs" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.21-logs" } + +# Locals +evm-coder = { default-features = false, path = '../../crates/evm-coder' } +pallet-common = { default-features = false, path = '../../pallets/common' } +pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' } +pallet-nonfungible = { default-features = false, path = '../../pallets/nonfungible' } +up-data-structs = { default-features = false, path = '../../primitives/data-structs' } + +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '3.1.2' + +[features] +default = ["std"] +std = [ + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "sp-std/std", + "sp-core/std", + "evm-coder/std", + "pallet-evm-coder-substrate/std", + "pallet-evm/std", +] --- /dev/null +++ b/pallets/evm-collection/src/eth.rs @@ -0,0 +1,169 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use core::marker::PhantomData; +use evm_coder::{abi::AbiWriter, execution::*, generate_stubgen, solidity_interface, types::*, ToLog}; +use ethereum as _; +use pallet_common::CollectionById; +use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder}; +use pallet_evm::{ + ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, + account::CrossAccountId, Pallet as PalletEvm, +}; +use sp_core::H160; +use up_data_structs::{ + CreateCollectionData, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, + MAX_COLLECTION_NAME_LENGTH, +}; +use crate::{Config, Pallet}; +use frame_support::traits::Get; + +use sp_std::vec::Vec; +use alloc::format; + +struct EvmCollection(SubstrateRecorder); +impl WithRecorder for EvmCollection { + fn recorder(&self) -> &SubstrateRecorder { + &self.0 + } + + fn into_recorder(self) -> SubstrateRecorder { + self.0 + } +} + +#[derive(ToLog)] +pub enum CollectionEvent { + CollectionCreated { + #[indexed] + owner: address, + #[indexed] + collection_id: address, + }, +} + +#[solidity_interface(name = "Collection")] +impl EvmCollection { + fn create_721_collection( + &self, + caller: caller, + name: string, + description: string, + token_prefix: string, + ) -> Result
{ + let caller = T::CrossAccountId::from_eth(caller); + let name = name + .encode_utf16() + .collect::>() + .try_into() + .map_err(|_| error_feild_too_long("name", MAX_COLLECTION_NAME_LENGTH))?; + let description = description + .encode_utf16() + .collect::>() + .try_into() + .map_err(|_| error_feild_too_long("description", MAX_COLLECTION_DESCRIPTION_LENGTH))?; + let token_prefix = token_prefix + .into_bytes() + .try_into() + .map_err(|_| error_feild_too_long("token_prefix", MAX_TOKEN_PREFIX_LENGTH))?; + + let data = CreateCollectionData { + name, + description, + token_prefix, + ..Default::default() + }; + + let collection_id = + >::init_collection(caller.as_sub().clone(), data) + .map_err(pallet_evm_coder_substrate::dispatch_to_evm::)?; + + let address = pallet_common::eth::collection_id_to_address(collection_id); + >::deposit_log( + CollectionEvent::CollectionCreated { + owner: *caller.as_eth(), + collection_id: address, + } + .to_log(address), + ); + Ok(address) + } + + // fn set_sponsor(collection_id: address, sponsor: address) -> Result { + // let collection_id = + // pallet_common::eth::map_eth_to_id(&collection_id).ok_or(Error::Revert("".into()))?; + // let mut collection = >::get(collection_id).ok_or(Error::Revert("".into()))?; + // let sponsor = T::CrossAccountId::from_eth(sponsor); + // collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.as_sub().clone()); + // >::insert(collection_id, collection); + // Ok(()) + // } + + // fn set_offchain_shema(shema: string) -> Result { + // Ok(()) + // } + + // fn set_const_on_chain_schema(shema: string) -> Result { + // Ok(()) + // } + + // fn set_variable_on_chain_schema(shema: string) -> Result { + // Ok(()) + // } + + // fn set_limits(limits: string) -> Result { + // Ok(()) + // } +} + +fn error_feild_too_long(feild: &str, bound: u32) -> Error { + Error::Revert(format!("{} is too long. Max length is {}.", feild, bound)) +} + +pub struct CollectionOnMethodCall(PhantomData<*const T>); +impl OnMethodCall for CollectionOnMethodCall { + fn is_reserved(contract: &sp_core::H160) -> bool { + contract == &T::ContractAddress::get() + } + + fn is_used(contract: &sp_core::H160) -> bool { + contract == &T::ContractAddress::get() + } + + fn call( + source: &sp_core::H160, + target: &sp_core::H160, + gas_left: u64, + input: &[u8], + value: sp_core::U256, + ) -> Option { + // TODO: Extract to another OnMethodCall handler + if target != &T::ContractAddress::get() { + return None; + } + + let helpers = EvmCollection::(SubstrateRecorder::::new(gas_left)); + pallet_evm_coder_substrate::call(*source, helpers, value, input) + } + + fn get_code(contract: &sp_core::H160) -> Option> { + (contract == &T::ContractAddress::get()) + .then(|| include_bytes!("./stubs/Collection.raw").to_vec()) + } +} + +generate_stubgen!(collection_impl, CollectionCall<()>, true); +generate_stubgen!(collection_iface, CollectionCall<()>, false); --- /dev/null +++ b/pallets/evm-collection/src/lib.rs @@ -0,0 +1,53 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use codec::{Decode, Encode, MaxEncodedLen}; +pub use pallet::*; +pub use eth::*; +use scale_info::TypeInfo; +pub mod eth; + +#[frame_support::pallet] +pub mod pallet { + pub use super::*; + use evm_coder::execution::Result; + use frame_support::pallet_prelude::*; + use sp_core::H160; + + #[pallet::config] + pub trait Config: + frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config + pallet_nonfungible::Config + { + type ContractAddress: Get; + } + + #[pallet::error] + pub enum Error { + /// This method is only executable by owner + NoPermission, + } + + #[pallet::pallet] + // #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + + impl Pallet {} +} --- /dev/null +++ b/pallets/evm-collection/src/stubs/Collection.sol @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +// Common stubs holder +contract Dummy { + uint8 dummy; + string stub_error = "this contract is implemented in native"; +} + +contract ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) + external + view + returns (bool) + { + require(false, stub_error); + interfaceID; + return true; + } +} + +// Selector: ee5467a8 +contract Collection is Dummy, ERC165 { + // Selector: contractOwner(address) 5152b14c + function contractOwner(address contractAddress) + public + view + returns (address) + { + require(false, stub_error); + contractAddress; + dummy; + return 0x0000000000000000000000000000000000000000; + } + + // Selector: sponsoringEnabled(address) 6027dc61 + function sponsoringEnabled(address contractAddress) + public + view + returns (bool) + { + require(false, stub_error); + contractAddress; + dummy; + return false; + } + + // Deprecated + // + // Selector: toggleSponsoring(address,bool) fcac6d86 + function toggleSponsoring(address contractAddress, bool enabled) public { + require(false, stub_error); + contractAddress; + enabled; + dummy = 0; + } + + // Selector: setSponsoringMode(address,uint8) fde8a560 + function setSponsoringMode(address contractAddress, uint8 mode) public { + require(false, stub_error); + contractAddress; + mode; + dummy = 0; + } + + // Selector: sponsoringMode(address) b70c7267 + function sponsoringMode(address contractAddress) + public + view + returns (uint8) + { + require(false, stub_error); + contractAddress; + dummy; + return 0; + } + + // Selector: setSponsoringRateLimit(address,uint32) 77b6c908 + function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) + public + { + require(false, stub_error); + contractAddress; + rateLimit; + dummy = 0; + } + + // Selector: getSponsoringRateLimit(address) 610cfabd + function getSponsoringRateLimit(address contractAddress) + public + view + returns (uint32) + { + require(false, stub_error); + contractAddress; + dummy; + return 0; + } + + // Selector: allowed(address,address) 5c658165 + function allowed(address contractAddress, address user) + public + view + returns (bool) + { + require(false, stub_error); + contractAddress; + user; + dummy; + return false; + } + + // Selector: allowlistEnabled(address) c772ef6c + function allowlistEnabled(address contractAddress) + public + view + returns (bool) + { + require(false, stub_error); + contractAddress; + dummy; + return false; + } + + // Selector: toggleAllowlist(address,bool) 36de20f5 + function toggleAllowlist(address contractAddress, bool enabled) public { + require(false, stub_error); + contractAddress; + enabled; + dummy = 0; + } + + // Selector: toggleAllowed(address,address,bool) 4706cc1c + function toggleAllowed( + address contractAddress, + address user, + bool allowed + ) public { + require(false, stub_error); + contractAddress; + user; + allowed; + dummy = 0; + } + + // Selector: create721Collection(string,string,string) 951c0151 + function create721Collection( + string memory name, + string memory description, + string memory tokenPrefix + ) public view returns (address) { + require(false, stub_error); + name; + description; + tokenPrefix; + dummy; + return 0x0000000000000000000000000000000000000000; + } +} --- a/pallets/evm-contract-helpers/src/eth.rs +++ b/pallets/evm-contract-helpers/src/eth.rs @@ -20,20 +20,15 @@ use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder}; use pallet_evm::{ ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, - account::CrossAccountId, Pallet as PalletEvm + account::CrossAccountId }; use sp_core::H160; -use up_data_structs::{ - CreateCollectionData, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, - MAX_COLLECTION_NAME_LENGTH, -}; use crate::{ AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT, }; use frame_support::traits::Get; use up_sponsorship::SponsorshipHandler; use sp_std::vec::Vec; -use alloc::format; struct ContractHelpers(SubstrateRecorder); impl WithRecorder for ContractHelpers { @@ -142,56 +137,6 @@ >::toggle_allowed(contract_address, user, allowed); Ok(()) } - - fn create_721_collection( - &self, - caller: caller, - name: string, - description: string, - token_prefix: string, - ) -> Result
{ - let caller = T::CrossAccountId::from_eth(caller); - let name = name - .encode_utf16() - .collect::>() - .try_into() - .map_err(|_| error_feild_too_long("name", MAX_COLLECTION_NAME_LENGTH))?; - let description = description - .encode_utf16() - .collect::>() - .try_into() - .map_err(|_| error_feild_too_long("description", MAX_COLLECTION_DESCRIPTION_LENGTH))?; - let token_prefix = token_prefix - .into_bytes() - .try_into() - .map_err(|_| error_feild_too_long("token_prefix", MAX_TOKEN_PREFIX_LENGTH))?; - - let data = CreateCollectionData { - name, - description, - token_prefix, - ..Default::default() - }; - - let collection_id = - >::init_collection(caller.as_sub().clone(), data) - .map_err(pallet_evm_coder_substrate::dispatch_to_evm::)?; - - let address = pallet_common::eth::collection_id_to_address(collection_id); - - >::deposit_log( - ContractHelperEvent::CollectionCreated { - owner: *caller.as_eth(), - collection_id: address, - } - .to_log(address), - ); - Ok(address) - } -} - -fn error_feild_too_long(feild: &str, bound: u32) -> Error { - Error::Revert(format!("{} is too long. Max length is {}.", feild, bound)) } pub struct HelpersOnMethodCall(PhantomData<*const T>); --- a/pallets/evm-contract-helpers/src/lib.rs +++ b/pallets/evm-contract-helpers/src/lib.rs @@ -16,9 +16,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[macro_use(format)] -extern crate alloc; - use codec::{Decode, Encode, MaxEncodedLen}; pub use pallet::*; pub use eth::*; --- /dev/null +++ b/pallets/evm-contract-helpers/src/stubs/Collection.sol @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +// Common stubs holder +contract Dummy { + uint8 dummy; + string stub_error = "this contract is implemented in native"; +} + +contract ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) + external + view + returns (bool) + { + require(false, stub_error); + interfaceID; + return true; + } +} + +// Selector: 61f17ed8 +contract ContractHelpers is Dummy, ERC165 { + // Selector: contractOwner(address) 5152b14c + function contractOwner(address contractAddress) + public + view + returns (address) + { + require(false, stub_error); + contractAddress; + dummy; + return 0x0000000000000000000000000000000000000000; + } + + // Selector: sponsoringEnabled(address) 6027dc61 + function sponsoringEnabled(address contractAddress) + public + view + returns (bool) + { + require(false, stub_error); + contractAddress; + dummy; + return false; + } + + // Deprecated + // + // Selector: toggleSponsoring(address,bool) fcac6d86 + function toggleSponsoring(address contractAddress, bool enabled) public { + require(false, stub_error); + contractAddress; + enabled; + dummy = 0; + } + + // Selector: setSponsoringMode(address,uint8) fde8a560 + function setSponsoringMode(address contractAddress, uint8 mode) public { + require(false, stub_error); + contractAddress; + mode; + dummy = 0; + } + + // Selector: sponsoringMode(address) b70c7267 + function sponsoringMode(address contractAddress) + public + view + returns (uint8) + { + require(false, stub_error); + contractAddress; + dummy; + return 0; + } + + // Selector: setSponsoringRateLimit(address,uint32) 77b6c908 + function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) + public + { + require(false, stub_error); + contractAddress; + rateLimit; + dummy = 0; + } + + // Selector: getSponsoringRateLimit(address) 610cfabd + function getSponsoringRateLimit(address contractAddress) + public + view + returns (uint32) + { + require(false, stub_error); + contractAddress; + dummy; + return 0; + } + + // Selector: allowed(address,address) 5c658165 + function allowed(address contractAddress, address user) + public + view + returns (bool) + { + require(false, stub_error); + contractAddress; + user; + dummy; + return false; + } + + // Selector: allowlistEnabled(address) c772ef6c + function allowlistEnabled(address contractAddress) + public + view + returns (bool) + { + require(false, stub_error); + contractAddress; + dummy; + return false; + } + + // Selector: toggleAllowlist(address,bool) 36de20f5 + function toggleAllowlist(address contractAddress, bool enabled) public { + require(false, stub_error); + contractAddress; + enabled; + dummy = 0; + } + + // Selector: toggleAllowed(address,address,bool) 4706cc1c + function toggleAllowed( + address contractAddress, + address user, + bool allowed + ) public { + require(false, stub_error); + contractAddress; + user; + allowed; + dummy = 0; + } + + // Selector: create721Collection(string,string,string) 951c0151 + function create721Collection( + string memory name, + string memory description, + string memory tokenPrefix + ) public view returns (address) { + require(false, stub_error); + name; + description; + tokenPrefix; + dummy; + return 0x0000000000000000000000000000000000000000; + } + + // Selector: setSponsor(address,address) f01fba93 + function setSponsor(address collectionId, address sponsor) public pure { + require(false, stub_error); + collectionId; + sponsor; + } + + // Selector: setOffchainShema(string) c3aa408b + function setOffchainShema(string memory shema) public pure { + require(false, stub_error); + shema; + } + + // Selector: setConstOnChainSchema(string) b284d8df + function setConstOnChainSchema(string memory shema) public pure { + require(false, stub_error); + shema; + } + + // Selector: setVariableOnChainSchema(string) 7c5f0fea + function setVariableOnChainSchema(string memory shema) public pure { + require(false, stub_error); + shema; + } + + // Selector: setLimits(string) 72cb345d + function setLimits(string memory limits) public pure { + require(false, stub_error); + limits; + } +} --- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol +++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol @@ -21,7 +21,7 @@ } } -// Selector: ee5467a8 +// Selector: 7b4866f9 contract ContractHelpers is Dummy, ERC165 { // Selector: contractOwner(address) 5152b14c function contractOwner(address contractAddress) @@ -143,19 +143,5 @@ user; allowed; dummy = 0; - } - - // Selector: create721Collection(string,string,string) 951c0151 - function create721Collection( - string memory name, - string memory description, - string memory tokenPrefix - ) public view returns (address) { - require(false, stub_error); - name; - description; - tokenPrefix; - dummy; - return 0x0000000000000000000000000000000000000000; } } --- a/runtime/opal/Cargo.toml +++ b/runtime/opal/Cargo.toml @@ -74,6 +74,7 @@ 'pallet-evm/std', 'pallet-evm-migration/std', 'pallet-evm-contract-helpers/std', + 'pallet-evm-collection/std', 'pallet-evm-transaction-payment/std', 'pallet-evm-coder-substrate/std', 'pallet-ethereum/std', @@ -417,6 +418,7 @@ pallet-charge-transaction = { git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.21", package = "pallet-template-transaction-payment", default-features = false, version = "3.0.0" } pallet-evm-migration = { path = '../../pallets/evm-migration', default-features = false } pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } +pallet-evm-collection = { path = '../../pallets/evm-collection', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.21-logs" } --- a/runtime/opal/src/lib.rs +++ b/runtime/opal/src/lib.rs @@ -49,7 +49,7 @@ // A few exports that help ease life for downstream crates. pub use pallet_balances::Call as BalancesCall; pub use pallet_evm::{ - EnsureAddressTruncated, HashedAddressMapping, Runner, account::CrossAccountId as _, + EnsureAddressTruncated, HashedAddressMapping, Runner, account::CrossAccountId as _, OnMethodCall, }; pub use frame_support::{ construct_runtime, match_types, @@ -306,6 +306,7 @@ pallet_evm_migration::OnMethodCall, pallet_evm_contract_helpers::HelpersOnMethodCall, CollectionDispatchT, + pallet_evm_collection::CollectionOnMethodCall, ); type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate; type ChainId = ChainId; @@ -974,6 +975,11 @@ pub const HelpersContractAddress: H160 = H160([ 0x84, 0x28, 0x99, 0xec, 0xf3, 0x80, 0x55, 0x3e, 0x8a, 0x4d, 0xe7, 0x5b, 0xf5, 0x34, 0xcd, 0xf6, 0xfb, 0xf6, 0x40, 0x49, ]); + + // 0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f + pub const EvmCollectionAddress: H160 = H160([ + 0x6c, 0x4e, 0x9f, 0xe1, 0xae, 0x37, 0xa4, 0x1e, 0x93, 0xce, 0xe4, 0x29, 0xe8, 0xe1, 0x88, 0x1a, 0xbd, 0xcb, 0xb5, 0x4f, + ]); } impl pallet_evm_contract_helpers::Config for Runtime { @@ -981,6 +987,10 @@ type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit; } +impl pallet_evm_collection::Config for Runtime { + type ContractAddress = EvmCollectionAddress; +} + construct_runtime!( pub enum Runtime where Block = Block, @@ -1033,6 +1043,7 @@ EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage} = 151, EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152, EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153, + EvmCollection: pallet_evm_collection::{Pallet} = 154, } ); --- a/runtime/quartz/Cargo.toml +++ b/runtime/quartz/Cargo.toml @@ -74,6 +74,7 @@ 'pallet-evm/std', 'pallet-evm-migration/std', 'pallet-evm-contract-helpers/std', + 'pallet-evm-collection/std', 'pallet-evm-transaction-payment/std', 'pallet-evm-coder-substrate/std', 'pallet-ethereum/std', @@ -422,6 +423,7 @@ pallet-charge-transaction = { git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.21", package = "pallet-template-transaction-payment", default-features = false, version = "3.0.0" } pallet-evm-migration = { path = '../../pallets/evm-migration', default-features = false } pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } +pallet-evm-collection = { path = '../../pallets/evm-collection', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.21-logs" } --- a/runtime/quartz/src/lib.rs +++ b/runtime/quartz/src/lib.rs @@ -79,7 +79,7 @@ }; use smallvec::smallvec; use codec::{Encode, Decode}; -use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping}; +use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping, OnMethodCall}; use fp_rpc::TransactionStatus; use sp_runtime::{ traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating}, @@ -278,6 +278,7 @@ pallet_evm_migration::OnMethodCall, pallet_evm_contract_helpers::HelpersOnMethodCall, CollectionDispatchT, + pallet_evm_collection::CollectionOnMethodCall, ); type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate; type ChainId = ChainId; @@ -951,6 +952,11 @@ pub const HelpersContractAddress: H160 = H160([ 0x84, 0x28, 0x99, 0xec, 0xf3, 0x80, 0x55, 0x3e, 0x8a, 0x4d, 0xe7, 0x5b, 0xf5, 0x34, 0xcd, 0xf6, 0xfb, 0xf6, 0x40, 0x49, ]); + + // 0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f + pub const EvmCollectionAddress: H160 = H160([ + 0x6c, 0x4e, 0x9f, 0xe1, 0xae, 0x37, 0xa4, 0x1e, 0x93, 0xce, 0xe4, 0x29, 0xe8, 0xe1, 0x88, 0x1a, 0xbd, 0xcb, 0xb5, 0x4f, + ]); } impl pallet_evm_contract_helpers::Config for Runtime { @@ -958,6 +964,10 @@ type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit; } +impl pallet_evm_collection::Config for Runtime { + type ContractAddress = EvmCollectionAddress; +} + construct_runtime!( pub enum Runtime where Block = Block, @@ -1010,6 +1020,7 @@ EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage} = 151, EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152, EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153, + EvmCollection: pallet_evm_collection::{Pallet} = 154, } ); --- a/runtime/unique/Cargo.toml +++ b/runtime/unique/Cargo.toml @@ -75,6 +75,7 @@ 'pallet-evm/std', 'pallet-evm-migration/std', 'pallet-evm-contract-helpers/std', + 'pallet-evm-collection/std', 'pallet-evm-transaction-payment/std', 'pallet-evm-coder-substrate/std', 'pallet-ethereum/std', @@ -414,6 +415,7 @@ pallet-charge-transaction = { git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.21", package = "pallet-template-transaction-payment", default-features = false, version = "3.0.0" } pallet-evm-migration = { path = '../../pallets/evm-migration', default-features = false } pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } +pallet-evm-collection = { path = '../../pallets/evm-collection', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.21-logs" } --- a/runtime/unique/src/lib.rs +++ b/runtime/unique/src/lib.rs @@ -49,7 +49,7 @@ // A few exports that help ease life for downstream crates. pub use pallet_balances::Call as BalancesCall; pub use pallet_evm::{ - EnsureAddressTruncated, HashedAddressMapping, Runner, account::CrossAccountId as _, + EnsureAddressTruncated, HashedAddressMapping, Runner, account::CrossAccountId as _, OnMethodCall, }; pub use frame_support::{ construct_runtime, match_types, @@ -282,6 +282,7 @@ pallet_evm_migration::OnMethodCall, pallet_evm_contract_helpers::HelpersOnMethodCall, CollectionDispatchT, + pallet_evm_collection::CollectionOnMethodCall, ); type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate; type ChainId = ChainId; @@ -956,6 +957,11 @@ pub const HelpersContractAddress: H160 = H160([ 0x84, 0x28, 0x99, 0xec, 0xf3, 0x80, 0x55, 0x3e, 0x8a, 0x4d, 0xe7, 0x5b, 0xf5, 0x34, 0xcd, 0xf6, 0xfb, 0xf6, 0x40, 0x49, ]); + + // 0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f + pub const EvmCollectionAddress: H160 = H160([ + 0x6c, 0x4e, 0x9f, 0xe1, 0xae, 0x37, 0xa4, 0x1e, 0x93, 0xce, 0xe4, 0x29, 0xe8, 0xe1, 0x88, 0x1a, 0xbd, 0xcb, 0xb5, 0x4f, + ]); } impl pallet_evm_contract_helpers::Config for Runtime { @@ -963,6 +969,10 @@ type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit; } +impl pallet_evm_collection::Config for Runtime { + type ContractAddress = EvmCollectionAddress; +} + construct_runtime!( pub enum Runtime where Block = Block, @@ -1015,6 +1025,7 @@ EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage} = 151, EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152, EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153, + EvmCollection: pallet_evm_collection::{Pallet} = 154, } ); --- /dev/null +++ b/tests/src/eth/api/Collection.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +// Common stubs holder +interface Dummy { + +} + +interface ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +// Selector: ee5467a8 +interface Collection is Dummy, ERC165 { + // Selector: contractOwner(address) 5152b14c + function contractOwner(address contractAddress) + external + view + returns (address); + + // Selector: sponsoringEnabled(address) 6027dc61 + function sponsoringEnabled(address contractAddress) + external + view + returns (bool); + + // Deprecated + // + // Selector: toggleSponsoring(address,bool) fcac6d86 + function toggleSponsoring(address contractAddress, bool enabled) external; + + // Selector: setSponsoringMode(address,uint8) fde8a560 + function setSponsoringMode(address contractAddress, uint8 mode) external; + + // Selector: sponsoringMode(address) b70c7267 + function sponsoringMode(address contractAddress) + external + view + returns (uint8); + + // Selector: setSponsoringRateLimit(address,uint32) 77b6c908 + function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) + external; + + // Selector: getSponsoringRateLimit(address) 610cfabd + function getSponsoringRateLimit(address contractAddress) + external + view + returns (uint32); + + // Selector: allowed(address,address) 5c658165 + function allowed(address contractAddress, address user) + external + view + returns (bool); + + // Selector: allowlistEnabled(address) c772ef6c + function allowlistEnabled(address contractAddress) + external + view + returns (bool); + + // Selector: toggleAllowlist(address,bool) 36de20f5 + function toggleAllowlist(address contractAddress, bool enabled) external; + + // Selector: toggleAllowed(address,address,bool) 4706cc1c + function toggleAllowed( + address contractAddress, + address user, + bool allowed + ) external; + + // Selector: create721Collection(string,string,string) 951c0151 + function create721Collection( + string memory name, + string memory description, + string memory tokenPrefix + ) external view returns (address); +} --- a/tests/src/eth/api/ContractHelpers.sol +++ b/tests/src/eth/api/ContractHelpers.sol @@ -12,7 +12,7 @@ function supportsInterface(bytes4 interfaceID) external view returns (bool); } -// Selector: ee5467a8 +// Selector: 7b4866f9 interface ContractHelpers is Dummy, ERC165 { // Selector: contractOwner(address) 5152b14c function contractOwner(address contractAddress) @@ -71,11 +71,4 @@ address user, bool allowed ) external; - - // Selector: create721Collection(string,string,string) 951c0151 - function create721Collection( - string memory name, - string memory description, - string memory tokenPrefix - ) external view returns (address); } --- /dev/null +++ b/tests/src/eth/collectionAbi.json @@ -0,0 +1,172 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "allowed", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "allowlistEnabled", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "contractOwner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "description", "type": "string" }, + { "internalType": "string", "name": "tokenPrefix", "type": "string" } + ], + "name": "create721Collection", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "getSponsoringRateLimit", + "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "uint8", "name": "mode", "type": "uint8" } + ], + "name": "setSponsoringMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "uint32", "name": "rateLimit", "type": "uint32" } + ], + "name": "setSponsoringRateLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "sponsoringEnabled", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "sponsoringMode", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "bool", "name": "allowed", "type": "bool" } + ], + "name": "toggleAllowed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "bool", "name": "enabled", "type": "bool" } + ], + "name": "toggleAllowlist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "bool", "name": "enabled", "type": "bool" } + ], + "name": "toggleSponsoring", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] --- a/tests/src/eth/createCollection.test.ts +++ b/tests/src/eth/createCollection.test.ts @@ -16,12 +16,12 @@ import {expect} from 'chai'; import {getCreatedCollectionCount, getDetailedCollectionInfo} from '../util/helpers'; -import {collectionIdFromAddress, contractHelpers, createEthAccountWithBalance, itWeb3} from './util/helpers'; +import {collectionHelper, collectionIdFromAddress, contractHelpers, createEthAccountWithBalance, itWeb3} from './util/helpers'; describe('Create collection from EVM', () => { itWeb3('Create collection', async ({api, web3}) => { const owner = await createEthAccountWithBalance(api, web3); - const helpers = contractHelpers(web3, owner); + const helpers = collectionHelper(web3, owner); const collectionName = 'CollectionEVM'; const description = 'Some description'; const tokenPrefix = 'token prefix'; --- a/tests/src/eth/util/contractHelpersAbi.json +++ b/tests/src/eth/util/contractHelpersAbi.json @@ -41,17 +41,6 @@ }, { "inputs": [ - { "internalType": "string", "name": "name", "type": "string" }, - { "internalType": "string", "name": "description", "type": "string" }, - { "internalType": "string", "name": "tokenPrefix", "type": "string" } - ], - "name": "create721Collection", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ { "internalType": "address", "name": "contractAddress", --- a/tests/src/eth/util/helpers.ts +++ b/tests/src/eth/util/helpers.ts @@ -28,6 +28,7 @@ import config from '../../config'; import privateKey from '../../substrate/privateKey'; import contractHelpersAbi from './contractHelpersAbi.json'; +import collectionAbi from '../collectionAbi.json'; import getBalance from '../../substrate/get-balance'; import waitNewBlocks from '../../substrate/wait-new-blocks'; @@ -273,6 +274,16 @@ return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS}); } +/** + * pallet evm_collection + * @param web3 + * @param caller - eth address + * @returns + */ +export function collectionHelper(web3: Web3, caller: string) { + return new web3.eth.Contract(collectionAbi as any, '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f', {from: caller, ...GAS_ARGS}); +} + /** * Execute ethereum method call using substrate account * @param to target contract