referrerpolicy=no-referrer-when-downgrade

pallet_revive/test_utils/
builder.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use super::{ETH_GAS_LIMIT, WEIGHT_LIMIT, deposit_limit};
19use crate::{
20	AccountIdOf, BalanceOf, Code, Config, ContractResult, ExecConfig, ExecReturnValue,
21	InstantiateReturnValue, OriginFor, Pallet, U256, Weight, address::AddressMapper,
22	evm::TransactionSigned, metering::TransactionLimits,
23};
24use alloc::{vec, vec::Vec};
25use frame_support::pallet_prelude::DispatchResultWithPostInfo;
26use paste::paste;
27use sp_core::H160;
28
29/// Helper macro to generate a builder for contract API calls.
30macro_rules! builder {
31	// Entry point to generate a builder for the given method.
32	(
33		$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
34        $($extra:item)*
35	) => {
36		paste!{
37			builder!(with_builder_fn, [< $method:camel Builder >], $method($($field: $type,)* ) -> $result; $($extra)*);
38		}
39	};
40	(
41		$full_expand:path,
42		$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
43        $($extra:item)*
44	) => {
45		paste!{
46			builder!($full_expand, [< $method:camel Builder >], $method($($field: $type,)* ) -> $result; $($extra)*);
47		}
48	};
49	// Generate the builder struct and its methods.
50	(
51		with_builder_fn,
52		$name:ident,
53		$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
54        $($extra:item)*
55	) => {
56		builder!(without_builder_fn, $name, $method($($field: $type,)* ) -> $result; $($extra)*);
57
58		#[allow(dead_code)]
59		impl<T: Config> $name<T> {
60			#[doc = concat!("Build the ", stringify!($method), " call")]
61			pub fn build(self) -> $result {
62				Pallet::<T>::$method(
63					$(self.$field,)*
64				)
65			}
66		}
67	};
68	// Generate the builder struct and its methods.
69	(
70		without_builder_fn,
71		$name:ident,
72		$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
73		$($extra:item)*
74	) => {
75		#[doc = concat!("A builder to construct a ", stringify!($method), " call")]
76		pub struct $name<T: Config> {
77			$($field: $type,)*
78		}
79
80		#[allow(dead_code)]
81		impl<T: Config> $name<T> {
82			$(
83				#[doc = concat!("Set the ", stringify!($field))]
84				pub fn $field(mut self, value: $type) -> Self {
85					self.$field = value;
86					self
87				}
88			)*
89
90			$($extra)*
91		}
92	};
93}
94
95pub struct Contract<T: Config> {
96	pub account_id: AccountIdOf<T>,
97	pub addr: H160,
98}
99
100builder!(
101	instantiate_with_code(
102		origin: OriginFor<T>,
103		value: BalanceOf<T>,
104		weight_limit: Weight,
105		storage_deposit_limit: BalanceOf<T>,
106		code: Vec<u8>,
107		data: Vec<u8>,
108		salt: Option<[u8; 32]>,
109	) -> DispatchResultWithPostInfo;
110
111	/// Create an [`InstantiateWithCodeBuilder`] with default values.
112	pub fn instantiate_with_code(origin: OriginFor<T>, code: Vec<u8>) -> Self {
113		Self {
114			origin,
115			value: 0u32.into(),
116			weight_limit: WEIGHT_LIMIT,
117			storage_deposit_limit: deposit_limit::<T>(),
118			code,
119			data: vec![],
120			salt: Some([0; 32]),
121		}
122	}
123);
124
125builder!(
126	instantiate(
127		origin: OriginFor<T>,
128		value: BalanceOf<T>,
129		weight_limit: Weight,
130		storage_deposit_limit: BalanceOf<T>,
131		code_hash: sp_core::H256,
132		data: Vec<u8>,
133		salt: Option<[u8; 32]>,
134	) -> DispatchResultWithPostInfo;
135
136	/// Create an [`InstantiateBuilder`] with default values.
137	pub fn instantiate(origin: OriginFor<T>, code_hash: sp_core::H256) -> Self {
138		Self {
139			origin,
140			value: 0u32.into(),
141			weight_limit: WEIGHT_LIMIT,
142			storage_deposit_limit: deposit_limit::<T>(),
143			code_hash,
144			data: vec![],
145			salt: Some([0; 32]),
146		}
147	}
148);
149
150builder!(
151	without_builder_fn,
152	bare_instantiate(
153		origin: OriginFor<T>,
154		evm_value: U256,
155		transaction_limits: TransactionLimits<T>,
156		code: Code,
157		data: Vec<u8>,
158		salt: Option<[u8; 32]>,
159		exec_config: ExecConfig<T>,
160	) -> ContractResult<InstantiateReturnValue, BalanceOf<T>>;
161
162	pub fn constructor_data(mut self, data: Vec<u8>) -> Self {
163		match self.code {
164			Code::Upload(ref mut code) if !code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
165				code.extend_from_slice(&data);
166				self
167			},
168			_ => {
169				self.data(data)
170			}
171		}
172	}
173
174	/// Build the "bare_instantiate" call
175	pub fn build(self) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
176		Pallet::<T>::bare_instantiate(
177			self.origin,
178			self.evm_value,
179			self.transaction_limits,
180			self.code,
181			self.data,
182			self.salt,
183			&self.exec_config
184		)
185	}
186	/// Set the call's evm_value using a native_value amount.
187	pub fn native_value(mut self, value: BalanceOf<T>) -> Self {
188		self.evm_value = Pallet::<T>::convert_native_to_evm(value);
189		self
190	}
191
192	/// Build the instantiate call and unwrap the result.
193	pub fn build_and_unwrap_result(self) -> InstantiateReturnValue {
194		self.build().result.unwrap()
195	}
196
197	/// Build the instantiate call and unwrap the account id.
198	pub fn build_and_unwrap_contract(self) -> Contract<T> {
199		let result = self.build().result.unwrap();
200		assert!(!result.result.did_revert(), "instantiation did revert");
201
202		let addr = result.addr;
203		let account_id = T::AddressMapper::to_account_id(&addr);
204		Contract{ account_id,  addr }
205	}
206
207	/// Create a [`BareInstantiateBuilder`] with default values.
208	pub fn bare_instantiate(origin: OriginFor<T>, code: Code) -> Self {
209		Self {
210			origin,
211			evm_value: Default::default(),
212			transaction_limits: TransactionLimits::WeightAndDeposit {
213				weight_limit: WEIGHT_LIMIT,
214				deposit_limit: deposit_limit::<T>()
215			},
216			code,
217			data: vec![],
218			salt: Some([0; 32]),
219			exec_config: ExecConfig::new_substrate_tx(),
220		}
221	}
222);
223
224builder!(
225	call(
226		origin: OriginFor<T>,
227		dest: H160,
228		value: BalanceOf<T>,
229		weight_limit: Weight,
230		storage_deposit_limit: BalanceOf<T>,
231		data: Vec<u8>,
232	) -> DispatchResultWithPostInfo;
233
234	/// Create a [`CallBuilder`] with default values.
235	pub fn call(origin: OriginFor<T>, dest: H160) -> Self {
236		CallBuilder {
237			origin,
238			dest,
239			value: 0u32.into(),
240			weight_limit: WEIGHT_LIMIT,
241			storage_deposit_limit: deposit_limit::<T>(),
242			data: vec![],
243		}
244	}
245);
246
247builder!(
248	without_builder_fn,
249	bare_call(
250		origin: OriginFor<T>,
251		dest: H160,
252		evm_value: U256,
253		transaction_limits: TransactionLimits<T>,
254		data: Vec<u8>,
255		exec_config: ExecConfig<T>,
256	) -> ContractResult<ExecReturnValue, BalanceOf<T>>;
257
258	/// Build the "bare_call" call
259	pub fn build(self) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
260		Pallet::<T>::bare_call(
261			self.origin,
262			self.dest,
263			self.evm_value,
264			self.transaction_limits,
265			self.data,
266			&self.exec_config
267		)
268	}
269
270	/// Set the call's evm_value using a native_value amount.
271	pub fn native_value(mut self, value: BalanceOf<T>) -> Self {
272		self.evm_value = Pallet::<T>::convert_native_to_evm(value);
273		self
274	}
275
276	/// Build the call and unwrap the result.
277	pub fn build_and_unwrap_result(self) -> ExecReturnValue {
278		self.build().result.unwrap()
279	}
280
281	/// Create a [`BareCallBuilder`] with default values.
282	pub fn bare_call(origin: OriginFor<T>, dest: H160) -> Self {
283		Self {
284			origin,
285			dest,
286			evm_value: Default::default(),
287			transaction_limits: TransactionLimits::WeightAndDeposit {
288				weight_limit: WEIGHT_LIMIT,
289				deposit_limit: deposit_limit::<T>()
290			},
291			data: vec![],
292			exec_config: ExecConfig::new_substrate_tx(),
293		}
294	}
295);
296
297builder!(
298	eth_call(
299		origin: OriginFor<T>,
300		dest: H160,
301		value: U256,
302		weight_limit: Weight,
303		eth_gas_limit: U256,
304		data: Vec<u8>,
305		transaction_encoded: Vec<u8>,
306		effective_gas_price: U256,
307		encoded_len: u32,
308	) -> DispatchResultWithPostInfo;
309
310	/// Create a [`EthCallBuilder`] with default values.
311	pub fn eth_call(origin: OriginFor<T>, dest: H160) -> Self {
312		Self {
313			origin,
314			dest,
315			value: 0u32.into(),
316			weight_limit: WEIGHT_LIMIT,
317			eth_gas_limit: ETH_GAS_LIMIT.into(),
318			data: vec![],
319			transaction_encoded: TransactionSigned::TransactionLegacySigned(Default::default()).signed_payload(),
320			effective_gas_price: 0u32.into(),
321			encoded_len: 0,
322		}
323	}
324);
325
326builder!(
327	eth_instantiate_with_code(
328			origin: OriginFor<T>,
329			value: U256,
330			gas_limit: Weight,
331			eth_gas_limit: U256,
332			code: Vec<u8>,
333			data: Vec<u8>,
334			transaction_encoded: Vec<u8>,
335			effective_gas_price: U256,
336			encoded_len: u32,
337	) -> DispatchResultWithPostInfo;
338
339	/// Create a [`EthInstantiateWithCodeBuilder`] with default values.
340	pub fn eth_instantiate_with_code(origin: OriginFor<T>, code: Vec<u8>) -> Self {
341		Self {
342			origin,
343			value: 0u32.into(),
344			gas_limit: WEIGHT_LIMIT,
345			eth_gas_limit: ETH_GAS_LIMIT.into(),
346			code,
347			data: vec![],
348			transaction_encoded: TransactionSigned::Transaction4844Signed(Default::default()).signed_payload(),
349			effective_gas_price: 0u32.into(),
350			encoded_len: 0,
351		}
352	}
353);