difftreelog
CORE-302 Add EvmCollection Pallet
in: master
24 files changed
Cargo.lockdiffbeforeafterboth--- 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",
Makefilediffbeforeafterboth--- 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:
pallets/evm-collection/Cargo.tomldiffbeforeafterboth--- /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",
+]
pallets/evm-collection/src/eth.rsdiffbeforeafterboth--- /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 <http://www.gnu.org/licenses/>.
+
+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<T: Config>(SubstrateRecorder<T>);
+impl<T: Config> WithRecorder<T> for EvmCollection<T> {
+ fn recorder(&self) -> &SubstrateRecorder<T> {
+ &self.0
+ }
+
+ fn into_recorder(self) -> SubstrateRecorder<T> {
+ self.0
+ }
+}
+
+#[derive(ToLog)]
+pub enum CollectionEvent {
+ CollectionCreated {
+ #[indexed]
+ owner: address,
+ #[indexed]
+ collection_id: address,
+ },
+}
+
+#[solidity_interface(name = "Collection")]
+impl<T: Config> EvmCollection<T> {
+ fn create_721_collection(
+ &self,
+ caller: caller,
+ name: string,
+ description: string,
+ token_prefix: string,
+ ) -> Result<address> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let name = name
+ .encode_utf16()
+ .collect::<Vec<u16>>()
+ .try_into()
+ .map_err(|_| error_feild_too_long("name", MAX_COLLECTION_NAME_LENGTH))?;
+ let description = description
+ .encode_utf16()
+ .collect::<Vec<u16>>()
+ .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 =
+ <pallet_nonfungible::Pallet<T>>::init_collection(caller.as_sub().clone(), data)
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+
+ let address = pallet_common::eth::collection_id_to_address(collection_id);
+ <PalletEvm<T>>::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<void> {
+ // let collection_id =
+ // pallet_common::eth::map_eth_to_id(&collection_id).ok_or(Error::Revert("".into()))?;
+ // let mut collection = <CollectionById<T>>::get(collection_id).ok_or(Error::Revert("".into()))?;
+ // let sponsor = T::CrossAccountId::from_eth(sponsor);
+ // collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.as_sub().clone());
+ // <CollectionById<T>>::insert(collection_id, collection);
+ // Ok(())
+ // }
+
+ // fn set_offchain_shema(shema: string) -> Result<void> {
+ // Ok(())
+ // }
+
+ // fn set_const_on_chain_schema(shema: string) -> Result<void> {
+ // Ok(())
+ // }
+
+ // fn set_variable_on_chain_schema(shema: string) -> Result<void> {
+ // Ok(())
+ // }
+
+ // fn set_limits(limits: string) -> Result<void> {
+ // 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<T: Config>(PhantomData<*const T>);
+impl<T: Config> OnMethodCall<T> for CollectionOnMethodCall<T> {
+ 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<PrecompileResult> {
+ // TODO: Extract to another OnMethodCall handler
+ if target != &T::ContractAddress::get() {
+ return None;
+ }
+
+ let helpers = EvmCollection::<T>(SubstrateRecorder::<T>::new(gas_left));
+ pallet_evm_coder_substrate::call(*source, helpers, value, input)
+ }
+
+ fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {
+ (contract == &T::ContractAddress::get())
+ .then(|| include_bytes!("./stubs/Collection.raw").to_vec())
+ }
+}
+
+generate_stubgen!(collection_impl, CollectionCall<()>, true);
+generate_stubgen!(collection_iface, CollectionCall<()>, false);
pallets/evm-collection/src/lib.rsdiffbeforeafterboth--- /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 <http://www.gnu.org/licenses/>.
+
+#![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<H160>;
+ }
+
+ #[pallet::error]
+ pub enum Error<T> {
+ /// This method is only executable by owner
+ NoPermission,
+ }
+
+ #[pallet::pallet]
+ // #[pallet::generate_store(pub(super) trait Store)]
+ pub struct Pallet<T>(_);
+
+
+ impl<T: Config> Pallet<T> {}
+}
pallets/evm-collection/src/stubs/Collection.rawdiffbeforeafterbothbinary blob — no preview
pallets/evm-collection/src/stubs/Collection.soldiffbeforeafterboth--- /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;
+ }
+}
pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- 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<T: Config>(SubstrateRecorder<T>);
impl<T: Config> WithRecorder<T> for ContractHelpers<T> {
@@ -142,56 +137,6 @@
<Pallet<T>>::toggle_allowed(contract_address, user, allowed);
Ok(())
}
-
- fn create_721_collection(
- &self,
- caller: caller,
- name: string,
- description: string,
- token_prefix: string,
- ) -> Result<address> {
- let caller = T::CrossAccountId::from_eth(caller);
- let name = name
- .encode_utf16()
- .collect::<Vec<u16>>()
- .try_into()
- .map_err(|_| error_feild_too_long("name", MAX_COLLECTION_NAME_LENGTH))?;
- let description = description
- .encode_utf16()
- .collect::<Vec<u16>>()
- .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 =
- <pallet_nonfungible::Pallet<T>>::init_collection(caller.as_sub().clone(), data)
- .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
-
- let address = pallet_common::eth::collection_id_to_address(collection_id);
-
- <PalletEvm<T>>::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<T: Config>(PhantomData<*const T>);
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- 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::*;
pallets/evm-contract-helpers/src/stubs/Collection.soldiffbeforeafterboth--- /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;
+ }
+}
pallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/evm-contract-helpers/src/stubs/ContractHelpers.soldiffbeforeafterboth--- 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;
}
}
runtime/opal/Cargo.tomldiffbeforeafterboth--- 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" }
runtime/opal/src/lib.rsdiffbeforeafterboth--- 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<Self>,
pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,
CollectionDispatchT<Self>,
+ pallet_evm_collection::CollectionOnMethodCall<Self>,
);
type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;
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,
}
);
runtime/quartz/Cargo.tomldiffbeforeafterboth--- 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" }
runtime/quartz/src/lib.rsdiffbeforeafterboth--- 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<Self>,
pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,
CollectionDispatchT<Self>,
+ pallet_evm_collection::CollectionOnMethodCall<Self>,
);
type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;
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,
}
);
runtime/unique/Cargo.tomldiffbeforeafterboth--- 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" }
runtime/unique/src/lib.rsdiffbeforeafterboth--- 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<Self>,
pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,
CollectionDispatchT<Self>,
+ pallet_evm_collection::CollectionOnMethodCall<Self>,
);
type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;
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,
}
);
tests/src/eth/api/Collection.soldiffbeforeafterboth--- /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);
+}
tests/src/eth/api/ContractHelpers.soldiffbeforeafterboth--- 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);
}
tests/src/eth/collectionAbi.jsondiffbeforeafterboth--- /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"
+ }
+]
tests/src/eth/createCollection.test.tsdiffbeforeafterboth--- 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';
tests/src/eth/util/contractHelpersAbi.jsondiffbeforeafterboth--- 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",
tests/src/eth/util/helpers.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// eslint-disable-next-line @typescript-eslint/triple-slash-reference18/// <reference path="helpers.d.ts" />1920import {ApiPromise} from '@polkadot/api';21import {addressToEvm, evmToAddress} from '@polkadot/util-crypto';22import Web3 from 'web3';23import usingApi, {submitTransactionAsync} from '../../substrate/substrate-api';24import {IKeyringPair} from '@polkadot/types/types';25import {expect} from 'chai';26import {CrossAccountId, getGenericResult, UNIQUE} from '../../util/helpers';27import * as solc from 'solc';28import config from '../../config';29import privateKey from '../../substrate/privateKey';30import contractHelpersAbi from './contractHelpersAbi.json';31import getBalance from '../../substrate/get-balance';32import waitNewBlocks from '../../substrate/wait-new-blocks';3334export const GAS_ARGS = {gas: 2500000};3536export enum SponsoringMode {37 Disabled = 0,38 Allowlisted = 1,39 Generous = 2,40}4142let web3Connected = false;43export async function usingWeb3<T>(cb: (web3: Web3) => Promise<T> | T): Promise<T> {44 if (web3Connected) throw new Error('do not nest usingWeb3 calls');45 web3Connected = true;4647 const provider = new Web3.providers.WebsocketProvider(config.substrateUrl);48 const web3 = new Web3(provider);4950 try {51 return await cb(web3);52 } finally {53 // provider.disconnect(3000, 'normal disconnect');54 provider.connection.close();55 web3Connected = false;56 }57}5859function encodeIntBE(v: number): number[] {60 if (v >= 0xffffffff || v < 0) throw new Error('id overflow');61 return [62 v >> 24,63 (v >> 16) & 0xff,64 (v >> 8) & 0xff,65 v & 0xff,66 ];67}6869export function collectionIdToAddress(collection: number): string {70 const buf = Buffer.from([0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,71 ...encodeIntBE(collection),72 ]);73 return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));74}75export function collectionIdFromAddress(address: string): number {76 return Number('0x' + address.substring(address.length - 8));77}7879export function tokenIdToAddress(collection: number, token: number): string {80 const buf = Buffer.from([0xf8, 0x23, 0x8c, 0xcf, 0xff, 0x8e, 0xd8, 0x87, 0x46, 0x3f, 0xd5, 0xe0,81 ...encodeIntBE(collection),82 ...encodeIntBE(token),83 ]);84 return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));85}86export function tokenIdToCross(collection: number, token: number): CrossAccountId {87 return {88 Ethereum: tokenIdToAddress(collection, token),89 };9091export function createEthAccount(web3: Web3) {92 const account = web3.eth.accounts.create();93 web3.eth.accounts.wallet.add(account.privateKey);94 return account.address;95}9697export async function createEthAccountWithBalance(api: ApiPromise, web3: Web3) {98 const alice = privateKey('//Alice');99 const account = createEthAccount(web3);100 await transferBalanceToEth(api, alice, account);101102 return account;103}104105export async function transferBalanceToEth(api: ApiPromise, source: IKeyringPair, target: string, amount = 1000n * UNIQUE) {106 const tx = api.tx.balances.transfer(evmToAddress(target), amount);107 const events = await submitTransactionAsync(source, tx);108 const result = getGenericResult(events);109 expect(result.success).to.be.true;110}111112export async function itWeb3(name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any, opts: { only?: boolean, skip?: boolean } = {}) {113 let i: any = it;114 if (opts.only) i = i.only;115 else if (opts.skip) i = i.skip;116 i(name, async () => {117 await usingApi(async api => {118 await usingWeb3(async web3 => {119 await cb({api, web3});120 });121 });122 });123}124itWeb3.only = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, {only: true});125itWeb3.skip = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, {skip: true});126127export async function generateSubstrateEthPair(web3: Web3) {128 const account = web3.eth.accounts.create();129 evmToAddress(account.address);130}131132type NormalizedEvent = {133 address: string,134 event: string,135 args: { [key: string]: string }136};137138export function normalizeEvents(events: any): NormalizedEvent[] {139 const output = [];140 for (const key of Object.keys(events)) {141 if (key.match(/^[0-9]+$/)) {142 output.push(events[key]);143 } else if (Array.isArray(events[key])) {144 output.push(...events[key]);145 } else {146 output.push(events[key]);147 }148 }149 output.sort((a, b) => a.logIndex - b.logIndex);150 return output.map(({address, event, returnValues}) => {151 const args: { [key: string]: string } = {};152 for (const key of Object.keys(returnValues)) {153 if (!key.match(/^[0-9]+$/)) {154 args[key] = returnValues[key];155 }156 }157 return {158 address,159 event,160 args,161 };162 });163}164165export async function recordEvents(contract: any, action: () => Promise<void>): Promise<NormalizedEvent[]> {166 const out: any = [];167 contract.events.allEvents((_: any, event: any) => {168 out.push(event);169 });170 await action();171 return normalizeEvents(out);172}173174export function subToEthLowercase(eth: string): string {175 const bytes = addressToEvm(eth);176 return '0x' + Buffer.from(bytes).toString('hex');177}178179export function subToEth(eth: string): string {180 return Web3.utils.toChecksumAddress(subToEthLowercase(eth));181}182183export function compileContract(name: string, src: string) {184 const out = JSON.parse(solc.compile(JSON.stringify({185 language: 'Solidity',186 sources: {187 [`${name}.sol`]: {188 content: `189 // SPDX-License-Identifier: UNLICENSED190 pragma solidity ^0.8.6;191192 ${src}193 `,194 },195 },196 settings: {197 outputSelection: {198 '*': {199 '*': ['*'],200 },201 },202 },203 }))).contracts[`${name}.sol`][name];204205 return {206 abi: out.abi,207 object: '0x' + out.evm.bytecode.object,208 };209}210211export async function deployFlipper(web3: Web3, deployer: string) {212 const compiled = compileContract('Flipper', `213 contract Flipper {214 bool value = false;215 function flip() public {216 value = !value;217 }218 function getValue() public view returns (bool) {219 return value;220 }221 }222 `);223 const flipperContract = new web3.eth.Contract(compiled.abi, undefined, {224 data: compiled.object,225 from: deployer,226 ...GAS_ARGS,227 });228 const flipper = await flipperContract.deploy({data: compiled.object}).send({from: deployer});229230 return flipper;231}232233export async function deployCollector(web3: Web3, deployer: string) {234 const compiled = compileContract('Collector', `235 contract Collector {236 uint256 collected;237 fallback() external payable {238 giveMoney();239 }240 function giveMoney() public payable {241 collected += msg.value;242 }243 function getCollected() public view returns (uint256) {244 return collected;245 }246 function getUnaccounted() public view returns (uint256) {247 return address(this).balance - collected;248 }249250 function withdraw(address payable target) public {251 target.transfer(collected);252 collected = 0;253 }254 }255 `);256 const collectorContract = new web3.eth.Contract(compiled.abi, undefined, {257 data: compiled.object,258 from: deployer,259 ...GAS_ARGS,260 });261 const collector = await collectorContract.deploy({data: compiled.object}).send({from: deployer});262263 return collector;264}265266/** 267 * pallet evm_contract_helpers268 * @param web3 269 * @param caller - eth address270 * @returns 271 */272export function contractHelpers(web3: Web3, caller: string) {273 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});274}275276/**277 * Execute ethereum method call using substrate account278 * @param to target contract279 * @param mkTx - closure, receiving `contract.methods`, and returning method call,280 * to be used as following (assuming `to` = erc20 contract):281 * `m => m.transfer(to, amount)`282 *283 * # Example284 * ```ts285 * executeEthTxOnSub(api, alice, erc20Contract, m => m.transfer(target, amount));286 * ```287 */288export async function executeEthTxOnSub(web3: Web3, api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {289 const tx = api.tx.evm.call(290 subToEth(from.address),291 to.options.address,292 mkTx(to.methods).encodeABI(),293 value,294 GAS_ARGS.gas,295 await web3.eth.getGasPrice(),296 null,297 null,298 [],299 );300 const events = await submitTransactionAsync(from, tx);301 expect(events.some(({event: {section, method}}) => section == 'evm' && method == 'Executed')).to.be.true;302}303304export async function ethBalanceViaSub(api: ApiPromise, address: string): Promise<bigint> {305 return (await getBalance(api, [evmToAddress(address)]))[0];306}307308/**309 * Measure how much gas given closure consumes310 *311 * @param user which user balance will be checked312 */313export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {314 const before = await ethBalanceViaSub(api, user);315316 await call();317318 // In dev mode, the transaction might not finish processing in time319 await waitNewBlocks(api, 1);320 const after = await ethBalanceViaSub(api, user);321322 // Can't use .to.be.less, because chai doesn't supports bigint323 expect(after < before).to.be.true;324325 return before - after;326}327328type ElementOf<A> = A extends readonly (infer T)[] ? T : never;329// I want a fancier api, not a memory efficiency330export function* cartesian<T extends Array<Array<any>>, R extends Array<any>>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf<T[K]>}]> {331 if(args.length === 0) {332 yield internalRest as any;333 return;334 }335 for(const value of args[0]) {336 yield* cartesian([...internalRest, value], ...args.slice(1)) as any;337 }338}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// eslint-disable-next-line @typescript-eslint/triple-slash-reference18/// <reference path="helpers.d.ts" />1920import {ApiPromise} from '@polkadot/api';21import {addressToEvm, evmToAddress} from '@polkadot/util-crypto';22import Web3 from 'web3';23import usingApi, {submitTransactionAsync} from '../../substrate/substrate-api';24import {IKeyringPair} from '@polkadot/types/types';25import {expect} from 'chai';26import {CrossAccountId, getGenericResult, UNIQUE} from '../../util/helpers';27import * as solc from 'solc';28import config from '../../config';29import privateKey from '../../substrate/privateKey';30import contractHelpersAbi from './contractHelpersAbi.json';31import collectionAbi from '../collectionAbi.json';32import getBalance from '../../substrate/get-balance';33import waitNewBlocks from '../../substrate/wait-new-blocks';3435export const GAS_ARGS = {gas: 2500000};3637export enum SponsoringMode {38 Disabled = 0,39 Allowlisted = 1,40 Generous = 2,41}4243let web3Connected = false;44export async function usingWeb3<T>(cb: (web3: Web3) => Promise<T> | T): Promise<T> {45 if (web3Connected) throw new Error('do not nest usingWeb3 calls');46 web3Connected = true;4748 const provider = new Web3.providers.WebsocketProvider(config.substrateUrl);49 const web3 = new Web3(provider);5051 try {52 return await cb(web3);53 } finally {54 // provider.disconnect(3000, 'normal disconnect');55 provider.connection.close();56 web3Connected = false;57 }58}5960function encodeIntBE(v: number): number[] {61 if (v >= 0xffffffff || v < 0) throw new Error('id overflow');62 return [63 v >> 24,64 (v >> 16) & 0xff,65 (v >> 8) & 0xff,66 v & 0xff,67 ];68}6970export function collectionIdToAddress(collection: number): string {71 const buf = Buffer.from([0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,72 ...encodeIntBE(collection),73 ]);74 return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));75}76export function collectionIdFromAddress(address: string): number {77 return Number('0x' + address.substring(address.length - 8));78}7980export function tokenIdToAddress(collection: number, token: number): string {81 const buf = Buffer.from([0xf8, 0x23, 0x8c, 0xcf, 0xff, 0x8e, 0xd8, 0x87, 0x46, 0x3f, 0xd5, 0xe0,82 ...encodeIntBE(collection),83 ...encodeIntBE(token),84 ]);85 return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));86}87export function tokenIdToCross(collection: number, token: number): CrossAccountId {88 return {89 Ethereum: tokenIdToAddress(collection, token),90 };9192export function createEthAccount(web3: Web3) {93 const account = web3.eth.accounts.create();94 web3.eth.accounts.wallet.add(account.privateKey);95 return account.address;96}9798export async function createEthAccountWithBalance(api: ApiPromise, web3: Web3) {99 const alice = privateKey('//Alice');100 const account = createEthAccount(web3);101 await transferBalanceToEth(api, alice, account);102103 return account;104}105106export async function transferBalanceToEth(api: ApiPromise, source: IKeyringPair, target: string, amount = 1000n * UNIQUE) {107 const tx = api.tx.balances.transfer(evmToAddress(target), amount);108 const events = await submitTransactionAsync(source, tx);109 const result = getGenericResult(events);110 expect(result.success).to.be.true;111}112113export async function itWeb3(name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any, opts: { only?: boolean, skip?: boolean } = {}) {114 let i: any = it;115 if (opts.only) i = i.only;116 else if (opts.skip) i = i.skip;117 i(name, async () => {118 await usingApi(async api => {119 await usingWeb3(async web3 => {120 await cb({api, web3});121 });122 });123 });124}125itWeb3.only = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, {only: true});126itWeb3.skip = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, {skip: true});127128export async function generateSubstrateEthPair(web3: Web3) {129 const account = web3.eth.accounts.create();130 evmToAddress(account.address);131}132133type NormalizedEvent = {134 address: string,135 event: string,136 args: { [key: string]: string }137};138139export function normalizeEvents(events: any): NormalizedEvent[] {140 const output = [];141 for (const key of Object.keys(events)) {142 if (key.match(/^[0-9]+$/)) {143 output.push(events[key]);144 } else if (Array.isArray(events[key])) {145 output.push(...events[key]);146 } else {147 output.push(events[key]);148 }149 }150 output.sort((a, b) => a.logIndex - b.logIndex);151 return output.map(({address, event, returnValues}) => {152 const args: { [key: string]: string } = {};153 for (const key of Object.keys(returnValues)) {154 if (!key.match(/^[0-9]+$/)) {155 args[key] = returnValues[key];156 }157 }158 return {159 address,160 event,161 args,162 };163 });164}165166export async function recordEvents(contract: any, action: () => Promise<void>): Promise<NormalizedEvent[]> {167 const out: any = [];168 contract.events.allEvents((_: any, event: any) => {169 out.push(event);170 });171 await action();172 return normalizeEvents(out);173}174175export function subToEthLowercase(eth: string): string {176 const bytes = addressToEvm(eth);177 return '0x' + Buffer.from(bytes).toString('hex');178}179180export function subToEth(eth: string): string {181 return Web3.utils.toChecksumAddress(subToEthLowercase(eth));182}183184export function compileContract(name: string, src: string) {185 const out = JSON.parse(solc.compile(JSON.stringify({186 language: 'Solidity',187 sources: {188 [`${name}.sol`]: {189 content: `190 // SPDX-License-Identifier: UNLICENSED191 pragma solidity ^0.8.6;192193 ${src}194 `,195 },196 },197 settings: {198 outputSelection: {199 '*': {200 '*': ['*'],201 },202 },203 },204 }))).contracts[`${name}.sol`][name];205206 return {207 abi: out.abi,208 object: '0x' + out.evm.bytecode.object,209 };210}211212export async function deployFlipper(web3: Web3, deployer: string) {213 const compiled = compileContract('Flipper', `214 contract Flipper {215 bool value = false;216 function flip() public {217 value = !value;218 }219 function getValue() public view returns (bool) {220 return value;221 }222 }223 `);224 const flipperContract = new web3.eth.Contract(compiled.abi, undefined, {225 data: compiled.object,226 from: deployer,227 ...GAS_ARGS,228 });229 const flipper = await flipperContract.deploy({data: compiled.object}).send({from: deployer});230231 return flipper;232}233234export async function deployCollector(web3: Web3, deployer: string) {235 const compiled = compileContract('Collector', `236 contract Collector {237 uint256 collected;238 fallback() external payable {239 giveMoney();240 }241 function giveMoney() public payable {242 collected += msg.value;243 }244 function getCollected() public view returns (uint256) {245 return collected;246 }247 function getUnaccounted() public view returns (uint256) {248 return address(this).balance - collected;249 }250251 function withdraw(address payable target) public {252 target.transfer(collected);253 collected = 0;254 }255 }256 `);257 const collectorContract = new web3.eth.Contract(compiled.abi, undefined, {258 data: compiled.object,259 from: deployer,260 ...GAS_ARGS,261 });262 const collector = await collectorContract.deploy({data: compiled.object}).send({from: deployer});263264 return collector;265}266267/** 268 * pallet evm_contract_helpers269 * @param web3 270 * @param caller - eth address271 * @returns 272 */273export function contractHelpers(web3: Web3, caller: string) {274 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});275}276277/** 278 * pallet evm_collection279 * @param web3 280 * @param caller - eth address281 * @returns 282 */283export function collectionHelper(web3: Web3, caller: string) {284 return new web3.eth.Contract(collectionAbi as any, '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f', {from: caller, ...GAS_ARGS});285}286287/**288 * Execute ethereum method call using substrate account289 * @param to target contract290 * @param mkTx - closure, receiving `contract.methods`, and returning method call,291 * to be used as following (assuming `to` = erc20 contract):292 * `m => m.transfer(to, amount)`293 *294 * # Example295 * ```ts296 * executeEthTxOnSub(api, alice, erc20Contract, m => m.transfer(target, amount));297 * ```298 */299export async function executeEthTxOnSub(web3: Web3, api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {300 const tx = api.tx.evm.call(301 subToEth(from.address),302 to.options.address,303 mkTx(to.methods).encodeABI(),304 value,305 GAS_ARGS.gas,306 await web3.eth.getGasPrice(),307 null,308 null,309 [],310 );311 const events = await submitTransactionAsync(from, tx);312 expect(events.some(({event: {section, method}}) => section == 'evm' && method == 'Executed')).to.be.true;313}314315export async function ethBalanceViaSub(api: ApiPromise, address: string): Promise<bigint> {316 return (await getBalance(api, [evmToAddress(address)]))[0];317}318319/**320 * Measure how much gas given closure consumes321 *322 * @param user which user balance will be checked323 */324export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {325 const before = await ethBalanceViaSub(api, user);326327 await call();328329 // In dev mode, the transaction might not finish processing in time330 await waitNewBlocks(api, 1);331 const after = await ethBalanceViaSub(api, user);332333 // Can't use .to.be.less, because chai doesn't supports bigint334 expect(after < before).to.be.true;335336 return before - after;337}338339type ElementOf<A> = A extends readonly (infer T)[] ? T : never;340// I want a fancier api, not a memory efficiency341export function* cartesian<T extends Array<Array<any>>, R extends Array<any>>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf<T[K]>}]> {342 if(args.length === 0) {343 yield internalRest as any;344 return;345 }346 for(const value of args[0]) {347 yield* cartesian([...internalRest, value], ...args.slice(1)) as any;348 }349}