1#![no_std]
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(feature = "alloc")]
7use alloc::vec::Vec;
8
9use bobcat_maths::U;
10
11use bobcat_entry::code_hash;
12
13#[allow(unused)]
14use bobcat_panic::panic_on_err_bad_decoding_bool;
15
16type Address = [u8; 20];
17
18#[cfg(target_arch = "wasm32")]
19mod impls {
20 #[link(wasm_import_module = "vm_hooks")]
21 unsafe extern "C" {
22 pub(crate) fn call_contract(
23 contract: *const u8,
24 calldata: *const u8,
25 calldata_len: usize,
26 value: *const u8,
27 gas: u64,
28 return_data_len: *mut usize,
29 ) -> u8;
30
31 pub(crate) fn static_call_contract(
32 contract: *const u8,
33 calldata: *const u8,
34 calldata_len: usize,
35 gas: u64,
36 return_data_len: *mut usize,
37 ) -> u8;
38
39 pub(crate) fn delegate_call_contract(
40 contract: *const u8,
41 calldata: *const u8,
42 calldata_len: usize,
43 gas: u64,
44 return_data_len: *mut usize,
45 ) -> u8;
46
47 pub(crate) fn read_return_data(dest: *mut u8, offset: usize, size: usize) -> usize;
48 }
49}
50
51#[cfg(not(target_arch = "wasm32"))]
52mod impls {
53 pub(crate) unsafe fn call_contract(
54 _contract: *const u8,
55 _calldata: *const u8,
56 _calldata_len: usize,
57 _value: *const u8,
58 _gas: u64,
59 _return_data_len: *mut usize,
60 ) -> u8 {
61 0
62 }
63
64 pub(crate) unsafe fn static_call_contract(
65 _contract: *const u8,
66 _calldata: *const u8,
67 _calldata_len: usize,
68 _gas: u64,
69 _return_data_len: *mut usize,
70 ) -> u8 {
71 0
72 }
73
74 pub(crate) unsafe fn delegate_call_contract(
75 _contract: *const u8,
76 _calldata: *const u8,
77 _calldata_len: usize,
78 _gas: u64,
79 _return_data_len: *mut usize,
80 ) -> u8 {
81 0
82 }
83
84 pub(crate) unsafe fn read_return_data(_: *mut u8, _: usize, _: usize) -> usize {
85 0
86 }
87}
88
89use impls::{
90 call_contract as call, delegate_call_contract as delegate_call,
91 static_call_contract as static_call,
92};
93
94pub fn addr_has_code(addr: Address) -> bool {
95 code_hash(addr)
98 != [
99 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182,
100 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112,
101 ]
102}
103
104macro_rules! generate_call_variants {
105 ($base_fn:ident, has_value) => {
106 generate_call_variants!(@impl $base_fn, value: &U);
107 };
108 ($base_fn:ident) => {
109 generate_call_variants!(@impl $base_fn,);
110 };
111 (@impl $base_fn:ident, $($value_param:ident: $value_ty:ty)?) => {
112 paste::paste! {
113 pub fn [<$base_fn _partial>](
116 contract: Address,
117 calldata: &[u8],
118 $($value_param: $value_ty,)?
119 gas: u64,
120 ) -> (bool, usize) {
121 let mut return_data_len = 0usize;
122 let status = unsafe {
123 $base_fn(
124 contract.as_ptr(),
125 calldata.as_ptr(),
126 calldata.len(),
127 $($value_param.as_ptr(),)?
128 gas,
129 &mut return_data_len as *mut usize,
130 )
131 };
132 (status == 0, return_data_len)
133 }
134
135 pub fn [<$base_fn _unit>](
138 contract: Address,
139 calldata: &[u8],
140 $($value_param: $value_ty,)?
141 gas: u64
142 ) -> bool {
143 [<$base_fn _partial>](contract, calldata, $($value_param,)? gas).0
144 }
145
146 pub fn [<$base_fn _unit_opt>](
149 contract: Address,
150 calldata: &[u8],
151 $($value_param: $value_ty,)?
152 gas: u64,
153 ) -> Option<()> {
154 if [<$base_fn _unit>](
155 contract,
156 calldata,
157 $($value_param,)?
158 gas,
159 ) {
160 Some(())
161 } else {
162 None
163 }
164 }
165
166 pub fn [<$base_fn _slice>]<const DATA_CAP: usize>(
170 contract: Address,
171 calldata: &[u8],
172 $($value_param: $value_ty,)?
173 gas: u64,
174 offset: usize,
175 ) -> (bool, usize, [u8; DATA_CAP]) {
176 let mut b = [0u8; DATA_CAP];
177 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
178 if !rc {
179 return (false, rd_len, b);
180 }
181 panic_on_err_bad_decoding_bool!(
182 rd_len > offset,
183 "offset greater than rd len ok"
184 );
185 let size = rd_len - offset;
186 panic_on_err_bad_decoding_bool!(
187 DATA_CAP >= size,
188 "not enough slice capacity"
189 );
190 unsafe { impls::read_return_data(b.as_mut_ptr(), offset, DATA_CAP) };
191 (rc, rd_len, b)
192 }
193
194 pub fn [<$base_fn _err_slice>]<const DATA_CAP: usize>(
204 contract: Address,
205 calldata: &[u8],
206 $($value_param: $value_ty,)?
207 gas: u64,
208 offset: usize,
209 ) -> (bool, usize, [u8; DATA_CAP]) {
210 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
211 let size = if rc {
213 panic_on_err_bad_decoding_bool!(
214 rd_len > offset,
215 "offset greater than rd len ok"
216 );
217 rd_len - offset
218 } else {
219 rd_len
220 };
221 panic_on_err_bad_decoding_bool!(
225 DATA_CAP >= size,
226 "not enough _slice capacity"
227 );
228 let mut b = [0u8; DATA_CAP];
229 unsafe { impls::read_return_data(b.as_mut_ptr(), offset, DATA_CAP) };
230 (rc, size, b)
231 }
232
233 pub fn [<$base_fn _word>](
236 contract: Address,
237 calldata: &[u8],
238 $($value_param: $value_ty,)?
239 gas: u64,
240 offset: usize,
241 ) -> (bool, U) {
242 let (rc, len, v) = [<$base_fn _slice>]::<32>(contract, calldata, $($value_param,)? gas, offset);
243 panic_on_err_bad_decoding_bool!(len == 32, "response didn't write 32");
244 (rc, U::from(v))
245 }
246
247 pub fn [<$base_fn _slice_opt>]<const DATA_CAP: usize>(
249 contract: Address,
250 calldata: &[u8],
251 $($value_param: $value_ty,)?
252 gas: u64,
253 offset: usize,
254 ) -> Option<(usize, [u8; DATA_CAP])> {
255 let (rc, len, c) = [<$base_fn _slice>]::<DATA_CAP>(
256 contract,
257 calldata,
258 $($value_param,)?
259 gas,
260 offset,
261 );
262 if rc {
263 Some((len, c))
264 } else {
265 None
266 }
267 }
268
269 pub fn [<$base_fn _slice_res>]<const DATA_CAP: usize>(
272 contract: Address,
273 calldata: &[u8],
274 $($value_param: $value_ty,)?
275 gas: u64,
276 offset: usize,
277 ) -> Result<(usize, [u8; DATA_CAP]), (usize, [u8; DATA_CAP])> {
278 let (rc, len, c) = [<$base_fn _slice>]::<DATA_CAP>(
279 contract,
280 calldata,
281 $($value_param,)?
282 gas,
283 offset,
284 );
285 if rc {
286 Ok((len, c))
287 } else {
288 Err((len, c))
289 }
290 }
291
292 pub fn [<safe_ $base_fn _slice>]<const DATA_CAP: usize>(
295 contract: Address,
296 calldata: &[u8],
297 $($value_param: $value_ty,)?
298 gas: u64,
299 offset: usize,
300 ) -> Option<(bool, usize, [u8; DATA_CAP])> {
301 if addr_has_code(contract) {
302 Some([<$base_fn _slice>]::<DATA_CAP>(
303 contract, calldata, $($value_param,)? gas, offset,
304 ))
305 } else {
306 None
307 }
308 }
309
310 pub fn [<$base_fn _bool>](
313 contract: Address,
314 calldata: &[u8],
315 $($value_param: $value_ty,)?
316 gas: u64,
317 ) -> bool {
318 let (rc, _, v) = [<$base_fn _slice>]::<1>(
319 contract, calldata, $($value_param,)? gas, 31,
320 );
321 rc && v[0] == 1
322 }
323
324 pub fn [<$base_fn _bool_opt>](
327 contract: Address,
328 calldata: &[u8],
329 $($value_param: $value_ty,)?
330 gas: u64,
331 ) -> Option<()> {
332 if [<$base_fn _bool>](contract, calldata, $($value_param,)? gas) {
333 Some(())
334 } else {
335 None
336 }
337 }
338
339 pub fn [<safe_ $base_fn _bool>](
344 contract: Address,
345 calldata: &[u8],
346 $($value_param: $value_ty,)?
347 gas: u64,
348 ) -> bool {
349 if addr_has_code(contract) {
350 match [<$base_fn _partial>](contract, calldata, $($value_param,)? gas) {
351 (true, 32) => {
352 let mut b = [0u8; 1];
353 unsafe { impls::read_return_data(b.as_mut_ptr(), 31, 1) };
354 b[0] == 1
355 }
356 (true, 0) => true,
357 (true, _) => panic_on_err_bad_decoding_bool!("word not returned"),
358 _ => false
359 }
360 } else {
361 false
362 }
363 }
364
365 pub fn [<safe_ $base_fn _bool_opt>](
368 contract: Address,
369 calldata: &[u8],
370 $($value_param: $value_ty,)?
371 gas: u64,
372 ) -> Option<()> {
373 if [<safe_ $base_fn _bool>](
374 contract,
375 calldata,
376 $($value_param,)?
377 gas
378 ) {
379 Some(())
380 } else {
381 None
382 }
383 }
384
385 #[cfg(feature = "alloc")]
388 pub fn [<$base_fn _vec>](
389 contract: Address,
390 calldata: &[u8],
391 $($value_param: $value_ty,)?
392 gas: u64,
393 offset: usize,
394 ) -> (bool, Vec<u8>) {
395 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
396 let size = rd_len - offset;
397 let mut b = Vec::with_capacity(size);
398 unsafe { b.set_len(size) }
399 unsafe { impls::read_return_data(b.as_mut_ptr(), offset, size) };
400 (rc, b)
401 }
402
403 #[cfg(feature = "alloc")]
405 pub fn [<$base_fn _vec_opt>](
406 contract: Address,
407 calldata: &[u8],
408 $($value_param: $value_ty,)?
409 gas: u64,
410 offset: usize,
411 ) -> Option<Vec<u8>> {
412 let (rc, v) = [<$base_fn _vec>](
413 contract,
414 calldata,
415 $($value_param,)?
416 gas,
417 offset,
418 );
419 if rc {
420 Some(v)
421 } else {
422 None
423 }
424 }
425
426 #[cfg(feature = "alloc")]
429 pub fn [<$base_fn _vec_res>](
430 contract: Address,
431 calldata: &[u8],
432 $($value_param: $value_ty,)?
433 gas: u64,
434 offset: usize,
435 ) -> Result<Vec<u8>, Vec<u8>> {
436 let (rc, rd) = [<$base_fn _vec>](
437 contract,
438 calldata,
439 $($value_param,)?
440 gas,
441 offset,
442 );
443 if rc {
444 Ok(rd)
445 } else {
446 Err(rd)
447 }
448 }
449
450 #[cfg(feature = "alloc")]
452 pub fn [<$base_fn _word_err_vec>](
453 contract: Address,
454 calldata: &[u8],
455 $($value_param: $value_ty,)?
456 gas: u64,
457 ) -> (bool, U, Option<Vec<u8>>) {
458 let (rc, v) = [<$base_fn _vec>](
464 contract,
465 calldata,
466 $($value_param,)?
467 gas,
468 0
469 );
470 if rc {
471 panic_on_err_bad_decoding_bool!(v.len() == 32, "not return for word");
472 let v: [u8; 32] = v.try_into().unwrap();
473 (rc, U::from(v), None)
474 } else {
475 (rc, U::ZERO, Some(v))
476 }
477 }
478
479 #[cfg(feature = "alloc")]
482 pub fn [<$base_fn _bool_err_vec>](
483 contract: Address,
484 calldata: &[u8],
485 $($value_param: $value_ty,)?
486 gas: u64
487 ) -> (bool, Option<Vec<u8>>) {
488 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
489 if rc {
490 panic_on_err_bad_decoding_bool!(rd_len == 32, "not return for bool word");
491 let mut b = [0u8; 1];
492 unsafe {
493 impls::read_return_data(b.as_mut_ptr(), 31, 1);
494 }
495 (b[0] == 1, None)
496 } else {
497 let mut b = Vec::with_capacity(rd_len);
498 unsafe {
499 impls::read_return_data(b.as_mut_ptr(), 0, rd_len);
500 b.set_len(rd_len);
501 }
502 (false, Some(b))
503 }
504 }
505
506 #[cfg(feature = "alloc")]
511 pub fn [<safe_ $base_fn _bool_err_vec>](
512 contract: Address,
513 calldata: &[u8],
514 $($value_param: $value_ty,)?
515 gas: u64
516 ) -> (bool, Option<Vec<u8>>) {
517 if !addr_has_code(contract) {
518 return (false, None)
519 }
520 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
521 match (rc, rd_len) {
522 (true, 32) => {
523 let mut b = [0u8; 1];
524 unsafe {
525 impls::read_return_data(b.as_mut_ptr(), 31, 1);
526 }
527 (b[0] == 1, None)
528 }
529 (true, 0) => (true, None),
530 (true, _) => panic_on_err_bad_decoding_bool!(
531 "word not returned for safe_ _bool_err_vec"
532 ),
533 (false, rd_len) => {
534 let mut b = Vec::with_capacity(rd_len);
535 unsafe {
536 impls::read_return_data(b.as_mut_ptr(), 0, rd_len);
537 b.set_len(rd_len);
538 }
539 (false, Some(b))
540 }
541 }
542 }
543
544 #[cfg(feature = "alloc")]
546 pub fn [<$base_fn _unit_err_vec>](
547 contract: Address,
548 calldata: &[u8],
549 $($value_param: $value_ty,)?
550 gas: u64
551 ) -> (bool, Option<Vec<u8>>) {
552 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
553 if rc {
554 (true, None)
555 } else {
556 let mut b = Vec::with_capacity(rd_len);
557 unsafe {
558 impls::read_return_data(b.as_mut_ptr(), 0, rd_len);
559 b.set_len(rd_len);
560 }
561 (false, Some(b))
562 }
563 }
564
565 #[cfg(feature = "alloc")]
567 pub fn [<$base_fn _unit_err_res>](
568 contract: Address,
569 calldata: &[u8],
570 $($value_param: $value_ty,)?
571 gas: u64
572 ) -> Result<(), Vec<u8>> {
573 match [<$base_fn _unit_err_vec>](
574 contract,
575 calldata,
576 $($value_param,)?
577 gas
578 )
579 {
580 (false, Some(e)) => Err(e),
581 (false, None) => {
582 unimplemented!()
584 }
585 (true, None) | (true, Some(_)) => Ok(())
586 }
587 }
588
589 #[cfg(feature = "alloc")]
592 pub fn [<safe_ $base_fn _unit_err_vec>](
593 contract: Address,
594 calldata: &[u8],
595 $($value_param: $value_ty,)?
596 gas: u64
597 ) -> (bool, Option<Vec<u8>>) {
598 if addr_has_code(contract) {
599 [<$base_fn _unit_err_vec>](contract, calldata, $($value_param,)? gas)
600 } else {
601 (false, None)
602 }
603 }
604
605 #[cfg(feature = "alloc")]
607 pub fn [<$base_fn _word_err_vec_res>](
608 contract: Address,
609 calldata: &[u8],
610 $($value_param: $value_ty,)?
611 gas: u64,
612 ) -> Result<U, Vec<u8>> {
613 let (rc, w, v) = [<$base_fn _word_err_vec>](
614 contract,
615 calldata,
616 $($value_param,)?
617 gas
618 );
619 if rc {
620 Ok(w)
621 } else {
622 Err(v.expect("vec not containing anything"))
623 }
624 }
625
626 #[cfg(feature = "alloc")]
628 pub fn [<safe_ $base_fn _vec>](
629 contract: Address,
630 calldata: &[u8],
631 $($value_param: $value_ty,)?
632 gas: u64,
633 offset: usize,
634 ) -> Option<(bool, Vec<u8>)> {
635 if addr_has_code(contract) {
636 Some([<$base_fn _vec>](contract, calldata, $($value_param,)? gas, offset))
637 } else {
638 None
639 }
640 }
641 }
642 };
643}
644
645generate_call_variants!(call, has_value);
646generate_call_variants!(static_call);
647generate_call_variants!(delegate_call);