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, offset: {offset}, rd len: {rd_len}, contract: {contract:?}, calldata: {calldata:?}"
131 );
132 let size = rd_len - offset;
133 panic_on_err_bad_decoding_bool!(
134 DATA_CAP >= size;
135 "not enough slice capacity, contract: {contract:?}, capacity: {DATA_CAP}, requested: {size}, calldata: {calldata:?}"
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, contract: {contract:?}, rd len: {rd_len}, offset: {offset}, calldata: {calldata:?}"
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, contract: {contract:?}, data cap: {DATA_CAP}, size: {size}, calldata: {calldata:?}"
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!(
191 len == 32;
192 "response didn't write 32 length, length: {len}, contract: {contract:?}, calldata: {calldata:?}"
193 );
194 (rc, U::from(v))
195 }
196
197 pub fn [<$base_fn _slice_opt>]<const DATA_CAP: usize>(
199 contract: Address,
200 calldata: &[u8],
201 $($value_param: $value_ty,)?
202 gas: u64,
203 offset: usize,
204 ) -> Option<(usize, [u8; DATA_CAP])> {
205 let (rc, len, c) = [<$base_fn _slice>]::<DATA_CAP>(
206 contract,
207 calldata,
208 $($value_param,)?
209 gas,
210 offset,
211 );
212 if rc {
213 Some((len, c))
214 } else {
215 None
216 }
217 }
218
219 pub fn [<$base_fn _slice_res>]<const DATA_CAP: usize>(
222 contract: Address,
223 calldata: &[u8],
224 $($value_param: $value_ty,)?
225 gas: u64,
226 offset: usize,
227 ) -> Result<(usize, [u8; DATA_CAP]), (usize, [u8; DATA_CAP])> {
228 let (rc, len, c) = [<$base_fn _slice>]::<DATA_CAP>(
229 contract,
230 calldata,
231 $($value_param,)?
232 gas,
233 offset,
234 );
235 if rc {
236 Ok((len, c))
237 } else {
238 Err((len, c))
239 }
240 }
241
242 pub fn [<safe_ $base_fn _slice>]<const DATA_CAP: usize>(
245 contract: Address,
246 calldata: &[u8],
247 $($value_param: $value_ty,)?
248 gas: u64,
249 offset: usize,
250 ) -> Option<(bool, usize, [u8; DATA_CAP])> {
251 if addr_has_code(contract) {
252 Some([<$base_fn _slice>]::<DATA_CAP>(
253 contract, calldata, $($value_param,)? gas, offset,
254 ))
255 } else {
256 None
257 }
258 }
259
260 pub fn [<$base_fn _bool>](
263 contract: Address,
264 calldata: &[u8],
265 $($value_param: $value_ty,)?
266 gas: u64,
267 ) -> bool {
268 let (rc, _, v) = [<$base_fn _slice>]::<1>(
269 contract, calldata, $($value_param,)? gas, 31,
270 );
271 rc && v[0] == 1
272 }
273
274 pub fn [<$base_fn _bool_opt>](
277 contract: Address,
278 calldata: &[u8],
279 $($value_param: $value_ty,)?
280 gas: u64,
281 ) -> Option<()> {
282 if [<$base_fn _bool>](contract, calldata, $($value_param,)? gas) {
283 Some(())
284 } else {
285 None
286 }
287 }
288
289 pub fn [<safe_ $base_fn _bool>](
294 contract: Address,
295 calldata: &[u8],
296 $($value_param: $value_ty,)?
297 gas: u64,
298 ) -> bool {
299 if addr_has_code(contract) {
300 match [<$base_fn _partial>](contract, calldata, $($value_param,)? gas) {
301 (true, 32) => {
302 let mut b = [0u8; 1];
303 unsafe { host::read_return_data(b.as_mut_ptr(), 31, 1) };
304 b[0] == 1
305 }
306 (true, 0) => true,
307 (true, _) => panic_on_err_bad_decoding_bool!(
308 "word not returned, contract: {contract:?}, calldata: {calldata:?}"
309 ),
310 _ => false
311 }
312 } else {
313 false
314 }
315 }
316
317 pub fn [<safe_ $base_fn _bool_opt>](
320 contract: Address,
321 calldata: &[u8],
322 $($value_param: $value_ty,)?
323 gas: u64,
324 ) -> Option<()> {
325 if [<safe_ $base_fn _bool>](
326 contract,
327 calldata,
328 $($value_param,)?
329 gas
330 ) {
331 Some(())
332 } else {
333 None
334 }
335 }
336
337 #[cfg(feature = "alloc")]
340 pub fn [<$base_fn _vec>](
341 contract: Address,
342 calldata: &[u8],
343 $($value_param: $value_ty,)?
344 gas: u64,
345 offset: usize,
346 ) -> (bool, Vec<u8>) {
347 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
348 let size = rd_len - offset;
349 let mut b = Vec::with_capacity(size);
350 unsafe { b.set_len(size) }
351 unsafe { host::read_return_data(b.as_mut_ptr(), offset, size) };
352 (rc, b)
353 }
354
355 #[cfg(feature = "alloc")]
357 pub fn [<$base_fn _vec_opt>](
358 contract: Address,
359 calldata: &[u8],
360 $($value_param: $value_ty,)?
361 gas: u64,
362 offset: usize,
363 ) -> Option<Vec<u8>> {
364 let (rc, v) = [<$base_fn _vec>](
365 contract,
366 calldata,
367 $($value_param,)?
368 gas,
369 offset,
370 );
371 if rc {
372 Some(v)
373 } else {
374 None
375 }
376 }
377
378 #[cfg(feature = "alloc")]
381 pub fn [<$base_fn _vec_res>](
382 contract: Address,
383 calldata: &[u8],
384 $($value_param: $value_ty,)?
385 gas: u64,
386 offset: usize,
387 ) -> Result<Vec<u8>, Vec<u8>> {
388 let (rc, rd) = [<$base_fn _vec>](
389 contract,
390 calldata,
391 $($value_param,)?
392 gas,
393 offset,
394 );
395 if rc {
396 Ok(rd)
397 } else {
398 Err(rd)
399 }
400 }
401
402 #[cfg(feature = "alloc")]
404 pub fn [<$base_fn _word_err_vec>](
405 contract: Address,
406 calldata: &[u8],
407 $($value_param: $value_ty,)?
408 gas: u64,
409 ) -> (bool, U, Option<Vec<u8>>) {
410 let (rc, v) = [<$base_fn _vec>](
416 contract,
417 calldata,
418 $($value_param,)?
419 gas,
420 0
421 );
422 if rc {
423 panic_on_err_bad_decoding_bool!(
424 v.len() == 32;
425 "not return for word, len: {}, contract: {}, calldata: {}",
426 v.len(),
427 const_hex::const_encode::<20, false>(&contract).as_str(),
428 const_hex::encode(calldata)
429 );
430 let v: [u8; 32] = v.try_into().unwrap();
431 (rc, U::from(v), None)
432 } else {
433 (rc, U::ZERO, Some(v))
434 }
435 }
436
437 #[cfg(feature = "alloc")]
440 pub fn [<$base_fn _bool_err_vec>](
441 contract: Address,
442 calldata: &[u8],
443 $($value_param: $value_ty,)?
444 gas: u64
445 ) -> (bool, Option<Vec<u8>>) {
446 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
447 if rc {
448 panic_on_err_bad_decoding_bool!(
449 rd_len == 32;
450 "bad length for bool word error vec, len: {rd_len}, contract: {}, calldata: {}",
451 const_hex::const_encode::<20, false>(&contract).as_str(),
452 const_hex::encode(calldata)
453 );
454 let mut b = [0u8; 1];
455 unsafe {
456 host::read_return_data(b.as_mut_ptr(), 31, 1);
457 }
458 (b[0] == 1, None)
459 } else {
460 let mut b = Vec::with_capacity(rd_len);
461 unsafe {
462 host::read_return_data(b.as_mut_ptr(), 0, rd_len);
463 b.set_len(rd_len);
464 }
465 (false, Some(b))
466 }
467 }
468
469 #[cfg(feature = "alloc")]
474 pub fn [<safe_ $base_fn _bool_err_vec>](
475 contract: Address,
476 calldata: &[u8],
477 $($value_param: $value_ty,)?
478 gas: u64
479 ) -> (bool, Option<Vec<u8>>) {
480 if !addr_has_code(contract) {
481 return (false, None)
482 }
483 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
484 match (rc, rd_len) {
485 (true, 32) => {
486 let mut b = [0u8; 1];
487 unsafe {
488 host::read_return_data(b.as_mut_ptr(), 31, 1);
489 }
490 (b[0] == 1, None)
491 }
492 (true, 0) => (true, None),
493 (true, _) => panic_on_err_bad_decoding_bool!(
494 "word not returned for safe_ _bool_err_vec, contract: {}, calldata: {}",
495 const_hex::const_encode::<20, false>(&contract).as_str(),
496 const_hex::encode(calldata)
497 ),
498 (false, rd_len) => {
499 let mut b = Vec::with_capacity(rd_len);
500 unsafe {
501 host::read_return_data(b.as_mut_ptr(), 0, rd_len);
502 b.set_len(rd_len);
503 }
504 (false, Some(b))
505 }
506 }
507 }
508
509 #[cfg(feature = "alloc")]
511 pub fn [<$base_fn _unit_err_vec>](
512 contract: Address,
513 calldata: &[u8],
514 $($value_param: $value_ty,)?
515 gas: u64
516 ) -> (bool, Option<Vec<u8>>) {
517 let (rc, rd_len) = [<$base_fn _partial>](contract, calldata, $($value_param,)? gas);
518 if rc {
519 (true, None)
520 } else {
521 let mut b = Vec::with_capacity(rd_len);
522 unsafe {
523 host::read_return_data(b.as_mut_ptr(), 0, rd_len);
524 b.set_len(rd_len);
525 }
526 (false, Some(b))
527 }
528 }
529
530 #[cfg(feature = "alloc")]
532 pub fn [<$base_fn _unit_err_res>](
533 contract: Address,
534 calldata: &[u8],
535 $($value_param: $value_ty,)?
536 gas: u64
537 ) -> Result<(), Vec<u8>> {
538 match [<$base_fn _unit_err_vec>](
539 contract,
540 calldata,
541 $($value_param,)?
542 gas
543 )
544 {
545 (false, Some(e)) => Err(e),
546 (false, None) => {
547 unimplemented!()
549 }
550 (true, None) | (true, Some(_)) => Ok(())
551 }
552 }
553
554 #[cfg(feature = "alloc")]
557 pub fn [<safe_ $base_fn _unit_err_vec>](
558 contract: Address,
559 calldata: &[u8],
560 $($value_param: $value_ty,)?
561 gas: u64
562 ) -> (bool, Option<Vec<u8>>) {
563 if addr_has_code(contract) {
564 [<$base_fn _unit_err_vec>](contract, calldata, $($value_param,)? gas)
565 } else {
566 (false, None)
567 }
568 }
569
570 #[cfg(feature = "alloc")]
572 pub fn [<$base_fn _word_err_vec_res>](
573 contract: Address,
574 calldata: &[u8],
575 $($value_param: $value_ty,)?
576 gas: u64,
577 ) -> Result<U, Vec<u8>> {
578 let (rc, w, v) = [<$base_fn _word_err_vec>](
579 contract,
580 calldata,
581 $($value_param,)?
582 gas
583 );
584 if rc {
585 Ok(w)
586 } else {
587 Err(v.expect("vec not containing anything"))
588 }
589 }
590
591 #[cfg(feature = "alloc")]
593 pub fn [<safe_ $base_fn _vec>](
594 contract: Address,
595 calldata: &[u8],
596 $($value_param: $value_ty,)?
597 gas: u64,
598 offset: usize,
599 ) -> Option<(bool, Vec<u8>)> {
600 if addr_has_code(contract) {
601 Some([<$base_fn _vec>](contract, calldata, $($value_param,)? gas, offset))
602 } else {
603 None
604 }
605 }
606 }
607 };
608}
609
610generate_call_variants!(call, has_value);
611generate_call_variants!(static_call);
612generate_call_variants!(delegate_call);