1use 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
29macro_rules! builder {
31 (
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 (
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 (
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 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 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 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 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 pub fn build_and_unwrap_result(self) -> InstantiateReturnValue {
194 self.build().result.unwrap()
195 }
196
197 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 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 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 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 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 pub fn build_and_unwrap_result(self) -> ExecReturnValue {
278 self.build().result.unwrap()
279 }
280
281 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 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 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);