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
13use bobcat_host as host;
14
15use host::{
16 call_contract as call, delegate_call_contract as delegate_call,
17 static_call_contract as static_call,
18};
19
20#[allow(unused)]
21use bobcat_panic::panic_on_err_bad_decoding_bool;
22
23type Address = [u8; 20];
24
25pub fn read_return_data_slice<const CAP: usize>(offset: usize, size: usize) -> ([u8; CAP], usize) {
26 let mut b = [0u8; CAP];
27 let rd = unsafe { host::read_return_data(b.as_mut_ptr(), offset, size) };
28 (b, rd)
29}
30
31#[cfg(feature = "alloc")]
32pub fn read_return_data_vec(offset: usize, size: usize) -> Vec<u8> {
33 let mut b = Vec::with_capacity(size);
34 let rd = unsafe { host::read_return_data(b.as_mut_ptr(), offset, size) };
35 unsafe {
36 b.set_len(rd);
37 }
38 b
39}
40
41pub fn addr_has_code(addr: Address) -> bool {
42 code_hash(addr).0
45 != [
46 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182,
47 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112,
48 ]
49}
50
51macro_rules! generate_call_variants {
52 ($base_fn:ident, has_value) => {
53 generate_call_variants!(@impl $base_fn, value: &U);
54 };
55 ($base_fn:ident) => {
56 generate_call_variants!(@impl $base_fn,);
57 };
58 (@impl $base_fn:ident, $($value_param:ident: $value_ty:ty)?) => {
59 paste::paste! {
60 pub fn [<$base_fn _partial>](
63 contract: Address,
64 calldata: &[u8],
65 $($value_param: $value_ty,)?
66 gas: u64,
67 ) -> (bool, usize) {
68 let mut return_data_len = 0usize;
69 let status = unsafe {
70 $base_fn(
71 contract.as_ptr(),
72 calldata.as_ptr(),
73 calldata.len(),
74 $($value_param.as_ptr(),)?
75 gas,
76 &mut return_data_len as *mut usize,
77 )
78 };
79 (status == 0, return_data_len)
80 }
81
82 pub fn [<$base_fn _unit>](
85 contract: Address,
86 calldata: &[u8],
87 $($value_param: $value_ty,)?
88 gas: u64
89 ) -> bool {
90 [<$base_fn _partial>](contract, calldata, $($value_param,)? gas).0
91 }
92
93 pub fn [<$base_fn _unit_opt>](
96 contract: Address,
97 calldata: &[u8],
98 $($value_param: $value_ty,)?
99 gas: u64,
100 ) -> Option<()> {
101 if [<$base_fn _unit>](
102 contract,
103 calldata,
104 $($value_param,)?
105 gas,
106 ) {
107 Some(())
108 } else {
109 None
110 }
111 }
112
113 pub fn [<$base_fn _slice>]<const DATA_CAP: usize>(
117 contract: Address,
118 calldata: &[u8],
119 $($value_param: $value_ty,)?
120 gas: u64,
121 offset: usize,
122 ) -> (bool, usize, [u8; DATA_CAP]) {
123 let mut b = [0u8; DATA_CAP];
124 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
125 if !rc {
126 return (false, rd_len, b);
127 }
128 panic_on_err_bad_decoding_bool!(
129 rd_len > offset,
130 "offset greater than rd len ok"
131 );
132 let size = rd_len - offset;
133 panic_on_err_bad_decoding_bool!(
134 DATA_CAP >= size,
135 "not enough slice capacity"
136 );
137 unsafe { host::read_return_data(b.as_mut_ptr(), offset, DATA_CAP) };
138 (rc, rd_len, b)
139 }
140
141 pub fn [<$base_fn _err_slice>]<const DATA_CAP: usize>(
151 contract: Address,
152 calldata: &[u8],
153 $($value_param: $value_ty,)?
154 gas: u64,
155 offset: usize,
156 ) -> (bool, usize, [u8; DATA_CAP]) {
157 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
158 let size = if rc {
160 panic_on_err_bad_decoding_bool!(
161 rd_len > offset,
162 "offset greater than rd len ok"
163 );
164 rd_len - offset
165 } else {
166 rd_len
167 };
168 panic_on_err_bad_decoding_bool!(
172 DATA_CAP >= size,
173 "not enough _slice capacity"
174 );
175 let mut b = [0u8; DATA_CAP];
176 unsafe { host::read_return_data(b.as_mut_ptr(), offset, DATA_CAP) };
177 (rc, size, b)
178 }
179
180 pub fn [<$base_fn _word>](
183 contract: Address,
184 calldata: &[u8],
185 $($value_param: $value_ty,)?
186 gas: u64,
187 offset: usize,
188 ) -> (bool, U) {
189 let (rc, len, v) = [<$base_fn _slice>]::<32>(contract, calldata, $($value_param,)? gas, offset);
190 panic_on_err_bad_decoding_bool!(len == 32, "response didn't write 32");
191 (rc, U::from(v))
192 }
193
194 pub fn [<$base_fn _slice_opt>]<const DATA_CAP: usize>(
196 contract: Address,
197 calldata: &[u8],
198 $($value_param: $value_ty,)?
199 gas: u64,
200 offset: usize,
201 ) -> Option<(usize, [u8; DATA_CAP])> {
202 let (rc, len, c) = [<$base_fn _slice>]::<DATA_CAP>(
203 contract,
204 calldata,
205 $($value_param,)?
206 gas,
207 offset,
208 );
209 if rc {
210 Some((len, c))
211 } else {
212 None
213 }
214 }
215
216 pub fn [<$base_fn _slice_res>]<const DATA_CAP: usize>(
219 contract: Address,
220 calldata: &[u8],
221 $($value_param: $value_ty,)?
222 gas: u64,
223 offset: usize,
224 ) -> Result<(usize, [u8; DATA_CAP]), (usize, [u8; DATA_CAP])> {
225 let (rc, len, c) = [<$base_fn _slice>]::<DATA_CAP>(
226 contract,
227 calldata,
228 $($value_param,)?
229 gas,
230 offset,
231 );
232 if rc {
233 Ok((len, c))
234 } else {
235 Err((len, c))
236 }
237 }
238
239 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 ) -> Option<(bool, usize, [u8; DATA_CAP])> {
248 if addr_has_code(contract) {
249 Some([<$base_fn _slice>]::<DATA_CAP>(
250 contract, calldata, $($value_param,)? gas, offset,
251 ))
252 } else {
253 None
254 }
255 }
256
257 pub fn [<$base_fn _bool>](
260 contract: Address,
261 calldata: &[u8],
262 $($value_param: $value_ty,)?
263 gas: u64,
264 ) -> bool {
265 let (rc, _, v) = [<$base_fn _slice>]::<1>(
266 contract, calldata, $($value_param,)? gas, 31,
267 );
268 rc && v[0] == 1
269 }
270
271 pub fn [<$base_fn _bool_opt>](
274 contract: Address,
275 calldata: &[u8],
276 $($value_param: $value_ty,)?
277 gas: u64,
278 ) -> Option<()> {
279 if [<$base_fn _bool>](contract, calldata, $($value_param,)? gas) {
280 Some(())
281 } else {
282 None
283 }
284 }
285
286 pub fn [<safe_ $base_fn _bool>](
291 contract: Address,
292 calldata: &[u8],
293 $($value_param: $value_ty,)?
294 gas: u64,
295 ) -> bool {
296 if addr_has_code(contract) {
297 match [<$base_fn _partial>](contract, calldata, $($value_param,)? gas) {
298 (true, 32) => {
299 let mut b = [0u8; 1];
300 unsafe { host::read_return_data(b.as_mut_ptr(), 31, 1) };
301 b[0] == 1
302 }
303 (true, 0) => true,
304 (true, _) => panic_on_err_bad_decoding_bool!("word not returned"),
305 _ => false
306 }
307 } else {
308 false
309 }
310 }
311
312 pub fn [<safe_ $base_fn _bool_opt>](
315 contract: Address,
316 calldata: &[u8],
317 $($value_param: $value_ty,)?
318 gas: u64,
319 ) -> Option<()> {
320 if [<safe_ $base_fn _bool>](
321 contract,
322 calldata,
323 $($value_param,)?
324 gas
325 ) {
326 Some(())
327 } else {
328 None
329 }
330 }
331
332 #[cfg(feature = "alloc")]
335 pub fn [<$base_fn _vec>](
336 contract: Address,
337 calldata: &[u8],
338 $($value_param: $value_ty,)?
339 gas: u64,
340 offset: usize,
341 ) -> (bool, Vec<u8>) {
342 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
343 let size = rd_len - offset;
344 let mut b = Vec::with_capacity(size);
345 unsafe { b.set_len(size) }
346 unsafe { host::read_return_data(b.as_mut_ptr(), offset, size) };
347 (rc, b)
348 }
349
350 #[cfg(feature = "alloc")]
352 pub fn [<$base_fn _vec_opt>](
353 contract: Address,
354 calldata: &[u8],
355 $($value_param: $value_ty,)?
356 gas: u64,
357 offset: usize,
358 ) -> Option<Vec<u8>> {
359 let (rc, v) = [<$base_fn _vec>](
360 contract,
361 calldata,
362 $($value_param,)?
363 gas,
364 offset,
365 );
366 if rc {
367 Some(v)
368 } else {
369 None
370 }
371 }
372
373 #[cfg(feature = "alloc")]
376 pub fn [<$base_fn _vec_res>](
377 contract: Address,
378 calldata: &[u8],
379 $($value_param: $value_ty,)?
380 gas: u64,
381 offset: usize,
382 ) -> Result<Vec<u8>, Vec<u8>> {
383 let (rc, rd) = [<$base_fn _vec>](
384 contract,
385 calldata,
386 $($value_param,)?
387 gas,
388 offset,
389 );
390 if rc {
391 Ok(rd)
392 } else {
393 Err(rd)
394 }
395 }
396
397 #[cfg(feature = "alloc")]
399 pub fn [<$base_fn _word_err_vec>](
400 contract: Address,
401 calldata: &[u8],
402 $($value_param: $value_ty,)?
403 gas: u64,
404 ) -> (bool, U, Option<Vec<u8>>) {
405 let (rc, v) = [<$base_fn _vec>](
411 contract,
412 calldata,
413 $($value_param,)?
414 gas,
415 0
416 );
417 if rc {
418 panic_on_err_bad_decoding_bool!(v.len() == 32, "not return for word");
419 let v: [u8; 32] = v.try_into().unwrap();
420 (rc, U::from(v), None)
421 } else {
422 (rc, U::ZERO, Some(v))
423 }
424 }
425
426 #[cfg(feature = "alloc")]
429 pub fn [<$base_fn _bool_err_vec>](
430 contract: Address,
431 calldata: &[u8],
432 $($value_param: $value_ty,)?
433 gas: u64
434 ) -> (bool, Option<Vec<u8>>) {
435 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
436 if rc {
437 panic_on_err_bad_decoding_bool!(rd_len == 32, "not return for bool word");
438 let mut b = [0u8; 1];
439 unsafe {
440 host::read_return_data(b.as_mut_ptr(), 31, 1);
441 }
442 (b[0] == 1, None)
443 } else {
444 let mut b = Vec::with_capacity(rd_len);
445 unsafe {
446 host::read_return_data(b.as_mut_ptr(), 0, rd_len);
447 b.set_len(rd_len);
448 }
449 (false, Some(b))
450 }
451 }
452
453 #[cfg(feature = "alloc")]
458 pub fn [<safe_ $base_fn _bool_err_vec>](
459 contract: Address,
460 calldata: &[u8],
461 $($value_param: $value_ty,)?
462 gas: u64
463 ) -> (bool, Option<Vec<u8>>) {
464 if !addr_has_code(contract) {
465 return (false, None)
466 }
467 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
468 match (rc, rd_len) {
469 (true, 32) => {
470 let mut b = [0u8; 1];
471 unsafe {
472 host::read_return_data(b.as_mut_ptr(), 31, 1);
473 }
474 (b[0] == 1, None)
475 }
476 (true, 0) => (true, None),
477 (true, _) => panic_on_err_bad_decoding_bool!(
478 "word not returned for safe_ _bool_err_vec"
479 ),
480 (false, rd_len) => {
481 let mut b = Vec::with_capacity(rd_len);
482 unsafe {
483 host::read_return_data(b.as_mut_ptr(), 0, rd_len);
484 b.set_len(rd_len);
485 }
486 (false, Some(b))
487 }
488 }
489 }
490
491 #[cfg(feature = "alloc")]
493 pub fn [<$base_fn _unit_err_vec>](
494 contract: Address,
495 calldata: &[u8],
496 $($value_param: $value_ty,)?
497 gas: u64
498 ) -> (bool, Option<Vec<u8>>) {
499 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
500 if rc {
501 (true, None)
502 } else {
503 let mut b = Vec::with_capacity(rd_len);
504 unsafe {
505 host::read_return_data(b.as_mut_ptr(), 0, rd_len);
506 b.set_len(rd_len);
507 }
508 (false, Some(b))
509 }
510 }
511
512 #[cfg(feature = "alloc")]
514 pub fn [<$base_fn _unit_err_res>](
515 contract: Address,
516 calldata: &[u8],
517 $($value_param: $value_ty,)?
518 gas: u64
519 ) -> Result<(), Vec<u8>> {
520 match [<$base_fn _unit_err_vec>](
521 contract,
522 calldata,
523 $($value_param,)?
524 gas
525 )
526 {
527 (false, Some(e)) => Err(e),
528 (false, None) => {
529 unimplemented!()
531 }
532 (true, None) | (true, Some(_)) => Ok(())
533 }
534 }
535
536 #[cfg(feature = "alloc")]
539 pub fn [<safe_ $base_fn _unit_err_vec>](
540 contract: Address,
541 calldata: &[u8],
542 $($value_param: $value_ty,)?
543 gas: u64
544 ) -> (bool, Option<Vec<u8>>) {
545 if addr_has_code(contract) {
546 [<$base_fn _unit_err_vec>](contract, calldata, $($value_param,)? gas)
547 } else {
548 (false, None)
549 }
550 }
551
552 #[cfg(feature = "alloc")]
554 pub fn [<$base_fn _word_err_vec_res>](
555 contract: Address,
556 calldata: &[u8],
557 $($value_param: $value_ty,)?
558 gas: u64,
559 ) -> Result<U, Vec<u8>> {
560 let (rc, w, v) = [<$base_fn _word_err_vec>](
561 contract,
562 calldata,
563 $($value_param,)?
564 gas
565 );
566 if rc {
567 Ok(w)
568 } else {
569 Err(v.expect("vec not containing anything"))
570 }
571 }
572
573 #[cfg(feature = "alloc")]
575 pub fn [<safe_ $base_fn _vec>](
576 contract: Address,
577 calldata: &[u8],
578 $($value_param: $value_ty,)?
579 gas: u64,
580 offset: usize,
581 ) -> Option<(bool, Vec<u8>)> {
582 if addr_has_code(contract) {
583 Some([<$base_fn _vec>](contract, calldata, $($value_param,)? gas, offset))
584 } else {
585 None
586 }
587 }
588 }
589 };
590}
591
592generate_call_variants!(call, has_value);
593generate_call_variants!(static_call);
594generate_call_variants!(delegate_call);