1#![no_std]
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(feature = "alloc")]
7use alloc::vec::Vec;
8
9use core::cmp::min;
10
11use bobcat_maths::U;
12
13pub type Address = [u8; 20];
14
15#[cfg(target_arch = "wasm32")]
16mod impls {
17 #[link(wasm_import_module = "vm_hooks")]
18 unsafe extern "C" {
19 pub(crate) fn call_contract(
20 contract: *const u8,
21 calldata: *const u8,
22 calldata_len: usize,
23 value: *const u8,
24 gas: u64,
25 return_data_len: *mut usize,
26 ) -> u8;
27
28 pub(crate) fn static_call_contract(
29 contract: *const u8,
30 calldata: *const u8,
31 calldata_len: usize,
32 gas: u64,
33 return_data_len: *mut usize,
34 ) -> u8;
35
36 pub(crate) fn delegate_call_contract(
37 contract: *const u8,
38 calldata: *const u8,
39 calldata_len: usize,
40 gas: u64,
41 return_data_len: *mut usize,
42 ) -> u8;
43
44 pub(crate) fn read_return_data(dest: *mut u8, offset: usize, size: usize) -> usize;
45
46 pub(crate) fn account_code_size(address: *const u8) -> usize;
47 }
48}
49
50#[cfg(not(target_arch = "wasm32"))]
51mod impls {
52 pub(crate) unsafe fn call_contract(
53 _contract: *const u8,
54 _calldata: *const u8,
55 _calldata_len: usize,
56 _value: *const u8,
57 _gas: u64,
58 _return_data_len: *mut usize,
59 ) -> u8 {
60 0
61 }
62
63 pub(crate) unsafe fn static_call_contract(
64 _contract: *const u8,
65 _calldata: *const u8,
66 _calldata_len: usize,
67 _gas: u64,
68 _return_data_len: *mut usize,
69 ) -> u8 {
70 0
71 }
72
73 pub(crate) unsafe fn delegate_call_contract(
74 _contract: *const u8,
75 _calldata: *const u8,
76 _calldata_len: usize,
77 _gas: u64,
78 _return_data_len: *mut usize,
79 ) -> u8 {
80 0
81 }
82
83 pub(crate) unsafe fn read_return_data(_: *mut u8, _: usize, _: usize) -> usize {
84 0
85 }
86
87 pub(crate) unsafe fn account_code_size(_: *const u8) -> usize {
88 0
89 }
90}
91
92use impls::{
93 call_contract as call, delegate_call_contract as delegate_call,
94 static_call_contract as static_call,
95};
96
97fn code_size(addr: Address) -> usize {
98 unsafe { impls::account_code_size(addr.as_ptr()) }
99}
100
101macro_rules! generate_call_variants {
102 ($base_fn:ident, has_value) => {
103 generate_call_variants!(@impl $base_fn, value: &U);
104 };
105 ($base_fn:ident) => {
106 generate_call_variants!(@impl $base_fn,);
107 };
108 (@impl $base_fn:ident, $($value_param:ident: $value_ty:ty)?) => {
109 paste::paste! {
110 pub fn [<$base_fn _partial>](
113 contract: Address,
114 calldata: &[u8],
115 $($value_param: $value_ty,)?
116 gas: u64,
117 ) -> (bool, usize) {
118 let mut return_data_len = 0usize;
119 let status = unsafe {
120 $base_fn(
121 contract.as_ptr(),
122 calldata.as_ptr(),
123 calldata.len(),
124 $($value_param.as_ptr(),)?
125 gas,
126 &mut return_data_len as *mut usize,
127 )
128 };
129 (status == 0, return_data_len)
130 }
131
132 pub fn [<$base_fn _unit>](
135 contract: Address,
136 calldata: &[u8],
137 $($value_param: $value_ty,)?
138 gas: u64
139 ) -> bool {
140 [<$base_fn _partial>](contract, calldata, $($value_param,)? gas).0
141 }
142
143 pub fn [<$base_fn _unit_opt>](
146 contract: Address,
147 calldata: &[u8],
148 $($value_param: $value_ty,)?
149 gas: u64,
150 ) -> Option<()> {
151 if [<$base_fn _unit>](
152 contract,
153 calldata,
154 $($value_param,)?
155 gas,
156 ) {
157 Some(())
158 } else {
159 None
160 }
161 }
162
163 pub fn [<$base_fn _slice>]<const DATA_CAP: usize>(
172 contract: Address,
173 calldata: &[u8],
174 $($value_param: $value_ty,)?
175 gas: u64,
176 offset: usize,
177 size: usize,
178 ) -> (bool, usize, [u8; DATA_CAP]) {
179 assert!(DATA_CAP >= size, "not enough cap for size");
180 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
181 assert!(rd_len > offset, "not enough data for offset");
182 let mut b = [0u8; DATA_CAP];
183 let size = min(size, rd_len - offset);
184 unsafe { impls::read_return_data(b.as_mut_ptr(), offset, size) };
185 (rc, size, b)
186 }
187
188 pub fn [<$base_fn _slice_opt>]<const DATA_CAP: usize>(
190 contract: Address,
191 calldata: &[u8],
192 $($value_param: $value_ty,)?
193 gas: u64,
194 offset: usize,
195 size: usize,
196 ) -> Option<(usize, [u8; DATA_CAP])> {
197 let (rc, len, c) = [<$base_fn _slice>]::<DATA_CAP>(
198 contract,
199 calldata,
200 $($value_param,)?
201 gas,
202 offset,
203 size,
204 );
205 if rc {
206 Some((len, c))
207 } else {
208 None
209 }
210 }
211
212 pub fn [<$base_fn _slice_res>]<const DATA_CAP: usize>(
215 contract: Address,
216 calldata: &[u8],
217 $($value_param: $value_ty,)?
218 gas: u64,
219 offset: usize,
220 size: usize,
221 ) -> Result<(usize, [u8; DATA_CAP]), (usize, [u8; DATA_CAP])> {
222 let (rc, len, c) = [<$base_fn _slice>]::<DATA_CAP>(
223 contract,
224 calldata,
225 $($value_param,)?
226 gas,
227 offset,
228 size,
229 );
230 if rc {
231 Ok((len, c))
232 } else {
233 Ok((len, c))
234 }
235 }
236
237 pub fn [<safe_ $base_fn _slice>]<const DATA_CAP: usize>(
242 contract: Address,
243 calldata: &[u8],
244 $($value_param: $value_ty,)?
245 gas: u64,
246 offset: usize,
247 size: usize,
248 ) -> Option<(bool, usize, [u8; DATA_CAP])> {
249 if code_size(contract) > 0 {
250 Some([<$base_fn _slice>]::<DATA_CAP>(
251 contract, calldata, $($value_param,)? gas, offset, size,
252 ))
253 } else {
254 None
255 }
256 }
257
258 pub fn [<$base_fn _bool>](
261 contract: Address,
262 calldata: &[u8],
263 $($value_param: $value_ty,)?
264 gas: u64,
265 ) -> bool {
266 let (rc, _, v) = [<$base_fn _slice>]::<1>(
267 contract, calldata, $($value_param,)? gas, 31, 1,
268 );
269 rc && v[0] == 1
270 }
271
272 pub fn [<$base_fn _bool_opt>](
275 contract: Address,
276 calldata: &[u8],
277 $($value_param: $value_ty,)?
278 gas: u64,
279 ) -> Option<()> {
280 if [<$base_fn _bool>](contract, calldata, $($value_param,)? gas) {
281 Some(())
282 } else {
283 None
284 }
285 }
286
287 pub fn [<safe_ $base_fn _bool>](
290 contract: Address,
291 calldata: &[u8],
292 $($value_param: $value_ty,)?
293 gas: u64,
294 ) -> bool {
295 if code_size(contract) > 0 {
296 let (rc, _, v) = [<$base_fn _slice>]::<1>(
297 contract, calldata, $($value_param,)? gas, 31, 1,
298 );
299 rc && v[0] == 1
300 } else {
301 false
302 }
303 }
304
305 pub fn [<safe_ $base_fn _bool_opt>](
308 contract: Address,
309 calldata: &[u8],
310 $($value_param: $value_ty,)?
311 gas: u64,
312 ) -> Option<()> {
313 if [<safe_ $base_fn _bool>](
314 contract,
315 calldata,
316 $($value_param,)?
317 gas
318 ) {
319 Some(())
320 } else {
321 None
322 }
323 }
324
325 #[cfg(feature = "alloc")]
328 pub fn [<$base_fn _vec>](
329 contract: Address,
330 calldata: &[u8],
331 $($value_param: $value_ty,)?
332 gas: u64,
333 offset: usize,
334 size: usize,
335 ) -> (bool, Vec<u8>) {
336 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
337 let size = min(size, rd_len - offset);
338 let mut b = Vec::with_capacity(size);
339 unsafe { b.set_len(size) }
340 unsafe { impls::read_return_data(b.as_mut_ptr(), offset, size) };
341 (rc, b)
342 }
343
344 #[cfg(feature = "alloc")]
346 pub fn [<$base_fn _vec_opt>](
347 contract: Address,
348 calldata: &[u8],
349 $($value_param: $value_ty,)?
350 gas: u64,
351 offset: usize,
352 size: usize,
353 ) -> Option<Vec<u8>> {
354 let (rc, v) = [<$base_fn _vec>](
355 contract,
356 calldata,
357 $($value_param,)?
358 gas,
359 offset,
360 size,
361 );
362 if rc {
363 Some(v)
364 } else {
365 None
366 }
367 }
368
369 #[cfg(feature = "alloc")]
372 pub fn [<$base_fn _vec_res>]<const DATA_CAP: usize>(
373 contract: Address,
374 calldata: &[u8],
375 $($value_param: $value_ty,)?
376 gas: u64,
377 offset: usize,
378 size: usize,
379 ) -> Result<Vec<u8>, Vec<u8>> {
380 let (rc, rd) = [<$base_fn _vec>](
381 contract,
382 calldata,
383 $($value_param,)?
384 gas,
385 offset,
386 size,
387 );
388 if rc {
389 Ok(rd)
390 } else {
391 Ok(rd)
392 }
393 }
394
395 #[cfg(feature = "alloc")]
397 pub fn [<safe_ $base_fn _vec>](
398 contract: Address,
399 calldata: &[u8],
400 $($value_param: $value_ty,)?
401 gas: u64,
402 offset: usize,
403 size: usize,
404 ) -> Option<(bool, Vec<u8>)> {
405 if code_size(contract) > 0 {
406 Some([<$base_fn _vec>](contract, calldata, $($value_param,)? gas, offset, size))
407 } else {
408 None
409 }
410 }
411 }
412 };
413}
414
415generate_call_variants!(call, has_value);
416generate_call_variants!(static_call);
417generate_call_variants!(delegate_call);