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