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::{deposit_limit, ETH_GAS_LIMIT, WEIGHT_LIMIT};
19use crate::{
20	address::AddressMapper, evm::TransactionSigned, metering::TransactionLimits, AccountIdOf,
21	BalanceOf, Code, Config, ContractResult, ExecConfig, ExecReturnValue, InstantiateReturnValue,
22	OriginFor, Pallet, Weight, U256,
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!([< $method:camel Builder >], $method($($field: $type,)* ) -> $result; $($extra)*);
38		}
39	};
40	// Generate the builder struct and its methods.
41	(
42		$name:ident,
43		$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
44        $($extra:item)*
45	) => {
46		#[doc = concat!("A builder to construct a ", stringify!($method), " call")]
47		pub struct $name<T: Config> {
48			$($field: $type,)*
49		}
50
51		#[allow(dead_code)]
52		impl<T: Config> $name<T> {
53			$(
54				#[doc = concat!("Set the ", stringify!($field))]
55				pub fn $field(mut self, value: $type) -> Self {
56					self.$field = value;
57					self
58				}
59			)*
60
61			#[doc = concat!("Build the ", stringify!($method), " call")]
62			pub fn build(self) -> $result {
63				Pallet::<T>::$method(
64					$(self.$field,)*
65				)
66			}
67
68            $($extra)*
69		}
70	}
71}
72
73pub struct Contract<T: Config> {
74	pub account_id: AccountIdOf<T>,
75	pub addr: H160,
76}
77
78builder!(
79	instantiate_with_code(
80		origin: OriginFor<T>,
81		value: BalanceOf<T>,
82		weight_limit: Weight,
83		storage_deposit_limit: BalanceOf<T>,
84		code: Vec<u8>,
85		data: Vec<u8>,
86		salt: Option<[u8; 32]>,
87	) -> DispatchResultWithPostInfo;
88
89	/// Create an [`InstantiateWithCodeBuilder`] with default values.
90	pub fn instantiate_with_code(origin: OriginFor<T>, code: Vec<u8>) -> Self {
91		Self {
92			origin,
93			value: 0u32.into(),
94			weight_limit: WEIGHT_LIMIT,
95			storage_deposit_limit: deposit_limit::<T>(),
96			code,
97			data: vec![],
98			salt: Some([0; 32]),
99		}
100	}
101);
102
103builder!(
104	instantiate(
105		origin: OriginFor<T>,
106		value: BalanceOf<T>,
107		weight_limit: Weight,
108		storage_deposit_limit: BalanceOf<T>,
109		code_hash: sp_core::H256,
110		data: Vec<u8>,
111		salt: Option<[u8; 32]>,
112	) -> DispatchResultWithPostInfo;
113
114	/// Create an [`InstantiateBuilder`] with default values.
115	pub fn instantiate(origin: OriginFor<T>, code_hash: sp_core::H256) -> Self {
116		Self {
117			origin,
118			value: 0u32.into(),
119			weight_limit: WEIGHT_LIMIT,
120			storage_deposit_limit: deposit_limit::<T>(),
121			code_hash,
122			data: vec![],
123			salt: Some([0; 32]),
124		}
125	}
126);
127
128builder!(
129	bare_instantiate(
130		origin: OriginFor<T>,
131		evm_value: U256,
132		transaction_limits: TransactionLimits<T>,
133		code: Code,
134		data: Vec<u8>,
135		salt: Option<[u8; 32]>,
136		exec_config: ExecConfig<T>,
137	) -> ContractResult<InstantiateReturnValue, BalanceOf<T>>;
138
139	pub fn constructor_data(mut self, data: Vec<u8>) -> Self {
140		match self.code {
141			Code::Upload(ref mut code) if !code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
142				code.extend_from_slice(&data);
143				self
144			},
145			_ => {
146				self.data(data)
147			}
148		}
149	}
150
151	/// Set the call's evm_value using a native_value amount.
152	pub fn native_value(mut self, value: BalanceOf<T>) -> Self {
153		self.evm_value = Pallet::<T>::convert_native_to_evm(value);
154		self
155	}
156
157	/// Build the instantiate call and unwrap the result.
158	pub fn build_and_unwrap_result(self) -> InstantiateReturnValue {
159		self.build().result.unwrap()
160	}
161
162	/// Build the instantiate call and unwrap the account id.
163	pub fn build_and_unwrap_contract(self) -> Contract<T> {
164		let result = self.build().result.unwrap();
165		assert!(!result.result.did_revert(), "instantiation did revert");
166
167		let addr = result.addr;
168		let account_id = T::AddressMapper::to_account_id(&addr);
169		Contract{ account_id,  addr }
170	}
171
172	/// Create a [`BareInstantiateBuilder`] with default values.
173	pub fn bare_instantiate(origin: OriginFor<T>, code: Code) -> Self {
174		Self {
175			origin,
176			evm_value: Default::default(),
177			transaction_limits: TransactionLimits::WeightAndDeposit {
178				weight_limit: WEIGHT_LIMIT,
179				deposit_limit: deposit_limit::<T>()
180			},
181			code,
182			data: vec![],
183			salt: Some([0; 32]),
184			exec_config: ExecConfig::new_substrate_tx(),
185		}
186	}
187);
188
189builder!(
190	call(
191		origin: OriginFor<T>,
192		dest: H160,
193		value: BalanceOf<T>,
194		weight_limit: Weight,
195		storage_deposit_limit: BalanceOf<T>,
196		data: Vec<u8>,
197	) -> DispatchResultWithPostInfo;
198
199	/// Create a [`CallBuilder`] with default values.
200	pub fn call(origin: OriginFor<T>, dest: H160) -> Self {
201		CallBuilder {
202			origin,
203			dest,
204			value: 0u32.into(),
205			weight_limit: WEIGHT_LIMIT,
206			storage_deposit_limit: deposit_limit::<T>(),
207			data: vec![],
208		}
209	}
210);
211
212builder!(
213	bare_call(
214		origin: OriginFor<T>,
215		dest: H160,
216		evm_value: U256,
217		transaction_limits: TransactionLimits<T>,
218		data: Vec<u8>,
219		exec_config: ExecConfig<T>,
220	) -> ContractResult<ExecReturnValue, BalanceOf<T>>;
221
222	/// Set the call's evm_value using a native_value amount.
223	pub fn native_value(mut self, value: BalanceOf<T>) -> Self {
224		self.evm_value = Pallet::<T>::convert_native_to_evm(value);
225		self
226	}
227
228	/// Build the call and unwrap the result.
229	pub fn build_and_unwrap_result(self) -> ExecReturnValue {
230		self.build().result.unwrap()
231	}
232
233	/// Create a [`BareCallBuilder`] with default values.
234	pub fn bare_call(origin: OriginFor<T>, dest: H160) -> Self {
235		Self {
236			origin,
237			dest,
238			evm_value: Default::default(),
239			transaction_limits: TransactionLimits::WeightAndDeposit {
240				weight_limit: WEIGHT_LIMIT,
241				deposit_limit: deposit_limit::<T>()
242			},
243			data: vec![],
244			exec_config: ExecConfig::new_substrate_tx(),
245		}
246	}
247);
248
249builder!(
250	eth_call(
251		origin: OriginFor<T>,
252		dest: H160,
253		value: U256,
254		weight_limit: Weight,
255		eth_gas_limit: U256,
256		data: Vec<u8>,
257		transaction_encoded: Vec<u8>,
258		effective_gas_price: U256,
259		encoded_len: u32,
260	) -> DispatchResultWithPostInfo;
261
262	/// Create a [`EthCallBuilder`] with default values.
263	pub fn eth_call(origin: OriginFor<T>, dest: H160) -> Self {
264		Self {
265			origin,
266			dest,
267			value: 0u32.into(),
268			weight_limit: WEIGHT_LIMIT,
269			eth_gas_limit: ETH_GAS_LIMIT.into(),
270			data: vec![],
271			transaction_encoded: TransactionSigned::TransactionLegacySigned(Default::default()).signed_payload(),
272			effective_gas_price: 0u32.into(),
273			encoded_len: 0,
274		}
275	}
276);
277
278builder!(
279	eth_instantiate_with_code(
280			origin: OriginFor<T>,
281			value: U256,
282			gas_limit: Weight,
283			eth_gas_limit: U256,
284			code: Vec<u8>,
285			data: Vec<u8>,
286			transaction_encoded: Vec<u8>,
287			effective_gas_price: U256,
288			encoded_len: u32,
289	) -> DispatchResultWithPostInfo;
290
291	/// Create a [`EthInstantiateWithCodeBuilder`] with default values.
292	pub fn eth_instantiate_with_code(origin: OriginFor<T>, code: Vec<u8>) -> Self {
293		Self {
294			origin,
295			value: 0u32.into(),
296			gas_limit: WEIGHT_LIMIT,
297			eth_gas_limit: ETH_GAS_LIMIT.into(),
298			code,
299			data: vec![],
300			transaction_encoded: TransactionSigned::Transaction4844Signed(Default::default()).signed_payload(),
301			effective_gas_price: 0u32.into(),
302			encoded_len: 0,
303		}
304	}
305);