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, GAS_LIMIT};
19use crate::{
20	address::AddressMapper, AccountIdOf, BalanceOf, BumpNonce, Code, Config, ContractResult,
21	DepositLimit, ExecReturnValue, InstantiateReturnValue, OriginFor, Pallet, Weight, U256,
22};
23use alloc::{vec, vec::Vec};
24use frame_support::pallet_prelude::DispatchResultWithPostInfo;
25use paste::paste;
26use sp_core::H160;
27
28/// Helper macro to generate a builder for contract API calls.
29macro_rules! builder {
30	// Entry point to generate a builder for the given method.
31	(
32		$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
33        $($extra:item)*
34	) => {
35		paste!{
36			builder!([< $method:camel Builder >], $method($($field: $type,)* ) -> $result; $($extra)*);
37		}
38	};
39	// Generate the builder struct and its methods.
40	(
41		$name:ident,
42		$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
43        $($extra:item)*
44	) => {
45		#[doc = concat!("A builder to construct a ", stringify!($method), " call")]
46		pub struct $name<T: Config> {
47			$($field: $type,)*
48		}
49
50		#[allow(dead_code)]
51		impl<T: Config> $name<T>
52		where
53			BalanceOf<T>: Into<sp_core::U256> + TryFrom<sp_core::U256>,
54			crate::MomentOf<T>: Into<sp_core::U256>,
55			T::Hash: frame_support::traits::IsType<sp_core::H256>,
56		{
57			$(
58				#[doc = concat!("Set the ", stringify!($field))]
59				pub fn $field(mut self, value: $type) -> Self {
60					self.$field = value;
61					self
62				}
63			)*
64
65			#[doc = concat!("Build the ", stringify!($method), " call")]
66			pub fn build(self) -> $result {
67				Pallet::<T>::$method(
68					$(self.$field,)*
69				)
70			}
71
72            $($extra)*
73		}
74	}
75}
76
77pub struct Contract<T: Config> {
78	pub account_id: AccountIdOf<T>,
79	pub addr: H160,
80}
81
82builder!(
83	instantiate_with_code(
84		origin: OriginFor<T>,
85		value: BalanceOf<T>,
86		gas_limit: Weight,
87		storage_deposit_limit: BalanceOf<T>,
88		code: Vec<u8>,
89		data: Vec<u8>,
90		salt: Option<[u8; 32]>,
91	) -> DispatchResultWithPostInfo;
92
93	/// Create an [`InstantiateWithCodeBuilder`] with default values.
94	pub fn instantiate_with_code(origin: OriginFor<T>, code: Vec<u8>) -> Self {
95		Self {
96			origin,
97			value: 0u32.into(),
98			gas_limit: GAS_LIMIT,
99			storage_deposit_limit: deposit_limit::<T>(),
100			code,
101			data: vec![],
102			salt: Some([0; 32]),
103		}
104	}
105);
106
107builder!(
108	instantiate(
109		origin: OriginFor<T>,
110		value: BalanceOf<T>,
111		gas_limit: Weight,
112		storage_deposit_limit: BalanceOf<T>,
113		code_hash: sp_core::H256,
114		data: Vec<u8>,
115		salt: Option<[u8; 32]>,
116	) -> DispatchResultWithPostInfo;
117
118	/// Create an [`InstantiateBuilder`] with default values.
119	pub fn instantiate(origin: OriginFor<T>, code_hash: sp_core::H256) -> Self {
120		Self {
121			origin,
122			value: 0u32.into(),
123			gas_limit: GAS_LIMIT,
124			storage_deposit_limit: deposit_limit::<T>(),
125			code_hash,
126			data: vec![],
127			salt: Some([0; 32]),
128		}
129	}
130);
131
132builder!(
133	bare_instantiate(
134		origin: OriginFor<T>,
135		evm_value: U256,
136		gas_limit: Weight,
137		storage_deposit_limit: DepositLimit<BalanceOf<T>>,
138		code: Code,
139		data: Vec<u8>,
140		salt: Option<[u8; 32]>,
141		bump_nonce: BumpNonce,
142	) -> ContractResult<InstantiateReturnValue, BalanceOf<T>>;
143
144	pub fn concat_evm_data(mut self, more_data: &[u8]) -> Self {
145		let Code::Upload(code) = &mut self.code else {
146			panic!("concat_evm_data should only be used with Code::Upload");
147		};
148		code.extend_from_slice(more_data);
149		self
150	}
151
152	/// Set the call's evm_value using a native_value amount.
153	pub fn native_value(mut self, value: BalanceOf<T>) -> Self {
154		self.evm_value = Pallet::<T>::convert_native_to_evm(value);
155		self
156	}
157
158	/// Build the instantiate call and unwrap the result.
159	pub fn build_and_unwrap_result(self) -> InstantiateReturnValue {
160		self.build().result.unwrap()
161	}
162
163	/// Build the instantiate call and unwrap the account id.
164	pub fn build_and_unwrap_contract(self) -> Contract<T> {
165		let result = self.build().result.unwrap();
166		assert!(!result.result.did_revert(), "instantiation did revert");
167
168		let addr = result.addr;
169		let account_id = T::AddressMapper::to_account_id(&addr);
170		Contract{ account_id,  addr }
171	}
172
173	/// Create a [`BareInstantiateBuilder`] with default values.
174	pub fn bare_instantiate(origin: OriginFor<T>, code: Code) -> Self {
175		Self {
176			origin,
177			evm_value: Default::default(),
178			gas_limit: GAS_LIMIT,
179			storage_deposit_limit: DepositLimit::Balance(deposit_limit::<T>()),
180			code,
181			data: vec![],
182			salt: Some([0; 32]),
183			bump_nonce: BumpNonce::Yes,
184		}
185	}
186);
187
188builder!(
189	call(
190		origin: OriginFor<T>,
191		dest: H160,
192		value: BalanceOf<T>,
193		gas_limit: Weight,
194		storage_deposit_limit: BalanceOf<T>,
195		data: Vec<u8>,
196	) -> DispatchResultWithPostInfo;
197
198	/// Create a [`CallBuilder`] with default values.
199	pub fn call(origin: OriginFor<T>, dest: H160) -> Self {
200		CallBuilder {
201			origin,
202			dest,
203			value: 0u32.into(),
204			gas_limit: GAS_LIMIT,
205			storage_deposit_limit: deposit_limit::<T>(),
206			data: vec![],
207		}
208	}
209);
210
211builder!(
212	bare_call(
213		origin: OriginFor<T>,
214		dest: H160,
215		evm_value: U256,
216		gas_limit: Weight,
217		storage_deposit_limit: DepositLimit<BalanceOf<T>>,
218		data: Vec<u8>,
219	) -> ContractResult<ExecReturnValue, BalanceOf<T>>;
220
221	/// Set the call's evm_value using a native_value amount.
222	pub fn native_value(mut self, value: BalanceOf<T>) -> Self {
223		self.evm_value = Pallet::<T>::convert_native_to_evm(value);
224		self
225	}
226
227	/// Build the call and unwrap the result.
228	pub fn build_and_unwrap_result(self) -> ExecReturnValue {
229		self.build().result.unwrap()
230	}
231
232	/// Create a [`BareCallBuilder`] with default values.
233	pub fn bare_call(origin: OriginFor<T>, dest: H160) -> Self {
234		Self {
235			origin,
236			dest,
237			evm_value: Default::default(),
238			gas_limit: GAS_LIMIT,
239			storage_deposit_limit: DepositLimit::Balance(deposit_limit::<T>()),
240			data: vec![],
241		}
242	}
243);
244
245builder!(
246	eth_call(
247		origin: OriginFor<T>,
248		dest: H160,
249		value: U256,
250		gas_limit: Weight,
251		storage_deposit_limit: BalanceOf<T>,
252		data: Vec<u8>,
253	) -> DispatchResultWithPostInfo;
254
255	/// Create a [`EthCallBuilder`] with default values.
256	pub fn eth_call(origin: OriginFor<T>, dest: H160) -> Self {
257		Self {
258			origin,
259			dest,
260			value: 0u32.into(),
261			gas_limit: GAS_LIMIT,
262			storage_deposit_limit: deposit_limit::<T>(),
263			data: vec![],
264		}
265	}
266);