git.delta.rocks / unique-network / refs/commits / dc6d7fbf1b22

difftreelog

CORE-302 Create collection from EVM principle

Trubnikov Sergey2022-04-13parent: #459d9af.patch.diff
in: master

9 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6078,18 +6078,22 @@
 name = "pallet-evm-contract-helpers"
 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 3.1.2",
  "scale-info",
  "sp-core",
  "sp-runtime",
  "sp-std",
+ "up-data-structs",
  "up-sponsorship",
 ]
 
modifiedpallets/evm-contract-helpers/Cargo.tomldiffbeforeafterboth
--- a/pallets/evm-contract-helpers/Cargo.toml
+++ b/pallets/evm-contract-helpers/Cargo.toml
@@ -8,18 +8,28 @@
 scale-info = { version = "2.0.1", default-features = false, features = [
     "derive",
 ] }
-frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.21" }
-frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.21" }
-sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.21" }
-sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.21" }
-sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.21" }
-evm-coder = { default-features = false, path = '../../crates/evm-coder' }
-pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
+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", branch = "unique-polkadot-v0.9.21-logs" }
 fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.21-logs" }
 up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.21" }
-log = "0.4.14"
 
+# 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']
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -15,11 +15,12 @@
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
 use core::marker::PhantomData;
-use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};
+use evm_coder::{abi::AbiWriter, execution::*, generate_stubgen, solidity_interface, types::*, ToLog};
+use ethereum as _;
 use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
 use pallet_evm::{
 	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure,
-	account::CrossAccountId,
+	account::CrossAccountId, Pallet as PalletEvm
 };
 use sp_core::H160;
 use crate::{
@@ -40,6 +41,16 @@
 	}
 }
 
+#[derive(ToLog)]
+pub enum ContractHelperEvent {
+	CollectionCreated {
+		#[indexed]
+		owner: address,
+		#[indexed]
+		collection_id: address,
+	},
+}
+
 #[solidity_interface(name = "ContractHelpers")]
 impl<T: Config> ContractHelpers<T> {
 	fn contract_owner(&self, contract_address: address) -> Result<address> {
@@ -126,6 +137,25 @@
 		<Pallet<T>>::toggle_allowed(contract_address, user, allowed);
 		Ok(())
 	}
+
+	fn create_721_collection(&self, caller: caller) -> Result<address> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let collection_id = <pallet_nonfungible::Pallet<T>>::init_collection(
+			caller.as_sub().clone(),
+			Default::default(),
+		)
+		.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)
+	}
 }
 
 pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);
modifiedpallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -31,7 +31,7 @@
 
 	#[pallet::config]
 	pub trait Config:
-		frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config
+		frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config + pallet_nonfungible::Config
 	{
 		type ContractAddress: Get<H160>;
 		type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;
modifiedpallets/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: 7b4866f9
+// Selector: e123b7a8
 contract ContractHelpers is Dummy, ERC165 {
 	// Selector: contractOwner(address) 5152b14c
 	function contractOwner(address contractAddress)
@@ -144,4 +144,11 @@
 		allowed;
 		dummy = 0;
 	}
+
+	// Selector: create721Collection() 9a6bd151
+	function create721Collection() public view returns (address) {
+		require(false, stub_error);
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
 }
modifiedtests/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: 7b4866f9
+// Selector: e123b7a8
 interface ContractHelpers is Dummy, ERC165 {
 	// Selector: contractOwner(address) 5152b14c
 	function contractOwner(address contractAddress)
@@ -71,4 +71,7 @@
 		address user,
 		bool allowed
 	) external;
+
+	// Selector: create721Collection() 9a6bd151
+	function create721Collection() external view returns (address);
 }
modifiedtests/src/eth/base.test.tsdiffbeforeafterboth
--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -14,9 +14,21 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, deployFlipper, ethBalanceViaSub, GAS_ARGS, itWeb3, recordEthFee, usingWeb3} from './util/helpers';
+import {
+  collectionIdFromAddress, 
+  collectionIdToAddress, 
+  contractHelpers, 
+  createEthAccount, 
+  createEthAccountWithBalance, 
+  deployFlipper, 
+  ethBalanceViaSub, 
+  GAS_ARGS, 
+  itWeb3, 
+  recordEthFee, 
+  usingWeb3,
+} from './util/helpers';
 import {expect} from 'chai';
-import {createCollectionExpectSuccess, createItemExpectSuccess, UNIQUE} from '../util/helpers';
+import {createCollectionExpectSuccess, createItemExpectSuccess, getCreatedCollectionCount, UNIQUE} from '../util/helpers';
 import nonFungibleAbi from './nonFungibleAbi.json';
 import privateKey from '../substrate/privateKey';
 import {Contract} from 'web3-eth-contract';
@@ -112,3 +124,18 @@
     expect(await contract(web3).methods.supportsInterface('0x01ffc9a7').call()).to.be.true;
   });
 });
+
+describe('Create collection from EVM', () => {
+  itWeb3('Create collection', async ({api, web3}) => {
+    const owner = await createEthAccountWithBalance(api, web3);
+    console.log(owner);
+    const helpers = contractHelpers(web3, owner);
+    const collectionCountBefore = await getCreatedCollectionCount(api);
+    const result = await helpers.methods.create721Collection().send();
+    console.log(result.events[0].raw);
+    const collectionId = collectionIdFromAddress(result.events[0].raw.topics[2]);
+    const collectionCountAfter = await getCreatedCollectionCount(api);
+    expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
+    expect(collectionId).to.be.eq(collectionCountAfter);
+  });
+});
modifiedtests/src/eth/util/contractHelpersAbi.jsondiffbeforeafterboth
before · tests/src/eth/util/contractHelpersAbi.json
1[2    {3        "inputs": [4            {5                "internalType": "address",6                "name": "contract",7                "type": "address"8            }9        ],10        "name": "allowlistEnabled",11        "outputs": [12            {13                "internalType": "bool",14                "name": "",15                "type": "bool"16            }17        ],18        "stateMutability": "view",19        "type": "function"20    },21    {22        "inputs": [23            {24                "internalType": "address",25                "name": "target",26                "type": "address"27            },28            {29                "internalType": "address",30                "name": "caller",31                "type": "address"32            }33        ],34        "name": "allowed",35        "outputs": [36            {37                "internalType": "bool",38                "name": "",39                "type": "bool"40            }41        ],42        "stateMutability": "view",43        "type": "function"44    },45    {46        "inputs": [47            {48                "internalType": "address",49                "name": "target",50                "type": "address"51            }52        ],53        "name": "contractOwner",54        "outputs": [55            {56                "internalType": "address",57                "name": "",58                "type": "address"59            }60        ],61        "stateMutability": "view",62        "type": "function"63    },64    {65        "inputs": [66            {67                "internalType": "address",68                "name": "target",69                "type": "address"70            }71        ],72        "name": "sponsoringEnabled",73        "outputs": [74            {75                "internalType": "bool",76                "name": "",77                "type": "bool"78            }79        ],80        "stateMutability": "view",81        "type": "function"82    },83    {84        "inputs": [85            {86                "internalType": "address",87                "name": "target",88                "type": "address"89            },90            {91                "internalType": "address",92                "name": "user",93                "type": "address"94            },95            {96                "internalType": "bool",97                "name": "isAllowed",98                "type": "bool"99            }100        ],101        "name": "toggleAllowed",102        "outputs": [],103        "stateMutability": "nonpayable",104        "type": "function"105    },106    {107        "inputs": [108            {109                "internalType": "address",110                "name": "target",111                "type": "address"112            },113            {114                "internalType": "bool",115                "name": "enabled",116                "type": "bool"117            }118        ],119        "name": "toggleAllowlist",120        "outputs": [],121        "stateMutability": "nonpayable",122        "type": "function"123    },124    {125        "inputs": [126            {127                "internalType": "address",128                "name": "target",129                "type": "address"130            },131            {132                "internalType": "bool",133                "name": "enabled",134                "type": "bool"135            }136        ],137        "name": "toggleSponsoring",138        "outputs": [],139        "stateMutability": "nonpayable",140        "type": "function"141    },142    {143        "inputs": [144            {145                "internalType": "address",146                "name": "target",147                "type": "address"148            },149            {150                "internalType": "uint8",151                "name": "mode",152                "type": "uint8"153            }154        ],155        "name": "setSponsoringMode",156        "outputs": [],157        "stateMutability": "nonpayable",158        "type": "function"159    },160    {161        "inputs": [162            {163                "internalType": "address",164                "name": "target",165                "type": "address"166            }167        ],168        "name": "sponsoringMode",169        "outputs": [170            {171                "internalType": "uint8",172                "name": "",173                "type": "uint8"174            }175        ],176        "stateMutability": "nonpayable",177        "type": "function"178    },179    {180        "inputs": [181            {182                "internalType": "address",183                "name": "target",184                "type": "address"185            },186            {187                "internalType": "uint32",188                "name": "limit",189                "type": "uint32"190            }191        ],192        "name": "setSponsoringRateLimit",193        "outputs": [],194        "stateMutability": "nonpayable",195        "type": "function"196    },197    {198        "inputs": [199            {200                "internalType": "address",201                "name": "contractAddress",202                "type": "address"203            }204        ],205        "name": "getSponsoringRateLimit",206        "outputs": [207            {208                "internalType": "uint32",209                "name": "",210                "type": "uint32"211            }212        ],213        "stateMutability": "view",214        "type": "function"215    }216]
after · tests/src/eth/util/contractHelpersAbi.json
1[2	{3		"inputs": [4			{5				"internalType": "address",6				"name": "contractAddress",7				"type": "address"8			},9			{10				"internalType": "address",11				"name": "user",12				"type": "address"13			}14		],15		"name": "allowed",16		"outputs": [17			{18				"internalType": "bool",19				"name": "",20				"type": "bool"21			}22		],23		"stateMutability": "view",24		"type": "function"25	},26	{27		"inputs": [28			{29				"internalType": "address",30				"name": "contractAddress",31				"type": "address"32			}33		],34		"name": "allowlistEnabled",35		"outputs": [36			{37				"internalType": "bool",38				"name": "",39				"type": "bool"40			}41		],42		"stateMutability": "view",43		"type": "function"44	},45	{46		"inputs": [47			{48				"internalType": "address",49				"name": "contractAddress",50				"type": "address"51			}52		],53		"name": "contractOwner",54		"outputs": [55			{56				"internalType": "address",57				"name": "",58				"type": "address"59			}60		],61		"stateMutability": "view",62		"type": "function"63	},64	{65		"inputs": [],66		"name": "create721Collection",67		"outputs": [68			{69				"internalType": "address",70				"name": "",71				"type": "address"72			}73		],74		"stateMutability": "view",75		"type": "function"76	},77	{78		"inputs": [79			{80				"internalType": "address",81				"name": "contractAddress",82				"type": "address"83			}84		],85		"name": "getSponsoringRateLimit",86		"outputs": [87			{88				"internalType": "uint32",89				"name": "",90				"type": "uint32"91			}92		],93		"stateMutability": "view",94		"type": "function"95	},96	{97		"inputs": [98			{99				"internalType": "address",100				"name": "contractAddress",101				"type": "address"102			},103			{104				"internalType": "uint8",105				"name": "mode",106				"type": "uint8"107			}108		],109		"name": "setSponsoringMode",110		"outputs": [],111		"stateMutability": "nonpayable",112		"type": "function"113	},114	{115		"inputs": [116			{117				"internalType": "address",118				"name": "contractAddress",119				"type": "address"120			},121			{122				"internalType": "uint32",123				"name": "rateLimit",124				"type": "uint32"125			}126		],127		"name": "setSponsoringRateLimit",128		"outputs": [],129		"stateMutability": "nonpayable",130		"type": "function"131	},132	{133		"inputs": [134			{135				"internalType": "address",136				"name": "contractAddress",137				"type": "address"138			}139		],140		"name": "sponsoringEnabled",141		"outputs": [142			{143				"internalType": "bool",144				"name": "",145				"type": "bool"146			}147		],148		"stateMutability": "view",149		"type": "function"150	},151	{152		"inputs": [153			{154				"internalType": "address",155				"name": "contractAddress",156				"type": "address"157			}158		],159		"name": "sponsoringMode",160		"outputs": [161			{162				"internalType": "uint8",163				"name": "",164				"type": "uint8"165			}166		],167		"stateMutability": "view",168		"type": "function"169	},170	{171		"inputs": [172			{173				"internalType": "bytes4",174				"name": "interfaceID",175				"type": "bytes4"176			}177		],178		"name": "supportsInterface",179		"outputs": [180			{181				"internalType": "bool",182				"name": "",183				"type": "bool"184			}185		],186		"stateMutability": "view",187		"type": "function"188	},189	{190		"inputs": [191			{192				"internalType": "address",193				"name": "contractAddress",194				"type": "address"195			},196			{197				"internalType": "address",198				"name": "user",199				"type": "address"200			},201			{202				"internalType": "bool",203				"name": "allowed",204				"type": "bool"205			}206		],207		"name": "toggleAllowed",208		"outputs": [],209		"stateMutability": "nonpayable",210		"type": "function"211	},212	{213		"inputs": [214			{215				"internalType": "address",216				"name": "contractAddress",217				"type": "address"218			},219			{220				"internalType": "bool",221				"name": "enabled",222				"type": "bool"223			}224		],225		"name": "toggleAllowlist",226		"outputs": [],227		"stateMutability": "nonpayable",228		"type": "function"229	},230	{231		"inputs": [232			{233				"internalType": "address",234				"name": "contractAddress",235				"type": "address"236			},237			{238				"internalType": "bool",239				"name": "enabled",240				"type": "bool"241			}242		],243		"name": "toggleSponsoring",244		"outputs": [],245		"stateMutability": "nonpayable",246		"type": "function"247	}248]
modifiedtests/src/eth/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/eth/util/helpers.ts
+++ b/tests/src/eth/util/helpers.ts
@@ -72,6 +72,9 @@
   ]);
   return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));
 }
+export function collectionIdFromAddress(address: string): number {
+  return Number('0x' + address.substring(address.length - 8));
+}
 
 export function tokenIdToAddress(collection: number, token: number): string {
   const buf = Buffer.from([0xf8, 0x23, 0x8c, 0xcf, 0xff, 0x8e, 0xd8, 0x87, 0x46, 0x3f, 0xd5, 0xe0,
@@ -84,7 +87,6 @@
   return {
     Ethereum: tokenIdToAddress(collection, token),
   };
-}
 
 export function createEthAccount(web3: Web3) {
   const account = web3.eth.accounts.create();