1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(feature = "alloc")]
7use alloc::{string::String, vec::Vec};
8
9pub use bobcat_maths::U;
10
11type Address = [u8; 20];
12
13pub use bobcat_cd::{EvmCdDeserialise, EvmCdError, read_words};
14
15pub use bobcat_host as host;
16
17use array_concat::concat_arrays;
18
19pub fn balance(addr: Address) -> U {
20 let mut out = U::ZERO;
21 unsafe { host::account_balance(addr.as_ptr(), out.as_mut_ptr()) }
22 out
23}
24
25#[unsafe(no_mangle)]
26#[cfg(all(
27 target_family = "wasm",
28 target_os = "unknown",
29 not(feature = "dont-define-symbols")
30))]
31pub unsafe fn mark_used() {
32 unsafe { host::pay_for_memory_grow(0) }
33 panic!();
34}
35
36pub fn write_slice(s: &[u8]) {
38 unsafe { host::write_result(s.as_ptr(), s.len()) }
39}
40
41#[deprecated = "Replaced by write_slice"]
42pub fn write_result_slice(x: &[u8]) {
43 write_slice(x)
44}
45
46pub fn write_word(s: &U) {
47 write_slice(&s.0)
48}
49
50#[deprecated = "Renamed to write_word"]
51pub fn write_result_word(x: &U) {
52 write_word(x)
53}
54
55pub fn write_bool(v: bool) {
56 write_slice(&U::from(v).0)
57}
58
59const OFFSET_ARR: [u8; 32] = U::from_u32(32).0;
60
61pub fn write_array_slice_len<const ARR_LEN: usize, const CD_LEN: usize>(
65 arr: [u8; ARR_LEN],
66 len_arr: usize,
67) {
68 assert!(
69 ARR_LEN + 32 * 2 == CD_LEN,
70 "bad array length, need: {}",
71 ARR_LEN + 32 * 2
72 );
73 assert!(
74 ARR_LEN >= len_arr,
75 "array length {len_arr} larger than buffer {ARR_LEN}"
76 );
77 let U(len_arr) = U::from_usize(ARR_LEN);
78 let x: [u8; CD_LEN] = concat_arrays!(OFFSET_ARR, len_arr, arr);
79 write_slice(&x)
80}
81
82pub fn write_array_slice<const ARR_LEN: usize, const CD_LEN: usize>(arr: [u8; ARR_LEN]) {
85 write_array_slice_len::<ARR_LEN, CD_LEN>(arr, ARR_LEN)
86}
87
88#[cfg(feature = "alloc")]
89pub fn write_array_vec(arr: Vec<u8>) {
90 let mut v = Vec::with_capacity(32 * 2 + arr.len());
91 v.extend_from_slice(&U::from_usize(32).0);
92 v.extend_from_slice(&U::from_usize(arr.len()).0);
93 v.extend(arr);
94 write_slice(&v)
95}
96
97#[cfg(feature = "alloc")]
100pub fn write_bytes(x: &[u8]) {
101 let mut v = Vec::with_capacity(32 * 2 + x.len());
102 v.extend_from_slice(&U::from_usize(32).0);
103 v.extend_from_slice(&U::from_usize(x.len()).0);
104 v.extend_from_slice(x);
105 write_slice(&v)
106}
107
108#[cfg(feature = "alloc")]
109pub fn write_string(x: String) {
110 write_array_vec(x.into_bytes())
111}
112
113pub const STR_BUFFER_SIZE: usize = 128 + 32 * 2;
115
116pub fn write_str(x: &str) {
119 let len = 64 + x.len().div_ceil(32) * 32;
120 assert!(len <= STR_BUFFER_SIZE, "str too large");
121 let mut buf = [0u8; STR_BUFFER_SIZE];
122 buf[31] = 0x20;
124 buf[32..32 * 2].copy_from_slice(U::from(x.len()).as_slice());
125 buf[32 * 2..32 * 2 + x.len()].copy_from_slice(x.as_bytes());
126 write_slice(&buf[..len])
127}
128
129pub fn return_data_size() -> usize {
130 unsafe { host::return_data_size() }
131}
132
133pub use bobcat_cd::leftpad_addr;
134
135#[macro_export]
139macro_rules! revert_if_bad_call_vec {
140 ($e:expr) => {{
141 let (rc, rd) = $e;
142 if !rc {
143 $crate::write_slice(&rd);
144 return 1;
145 }
146 rd
147 }};
148}
149
150#[macro_export]
153macro_rules! revert_if_bad_call_unit_vec {
154 ($e:expr) => {{
155 let (rc, revertdata) = $e;
156 match (rc, revertdata) {
157 (true, _) => (),
158 (false, Some(v)) => {
159 $crate::write_slice(&v);
160 return 1;
161 }
162 (false, _) => return 1,
163 }
164 }};
165}
166
167#[macro_export]
169macro_rules! revert_if_bad_call_slice_vec {
170 ($e:expr) => {{
171 let (rc, returndata, revertdata) = $e;
172 match (rc, revertdata) {
173 (true, _) => returndata,
174 (false, Some(v)) => {
175 $crate::write_slice(&v);
176 return 1;
177 }
178 (false, _) => return 1,
179 }
180 }};
181}
182
183#[macro_export]
184macro_rules! write_exit_res {
185 ($ident:expr) => {{
186 match $ident {
187 Ok(v) => {
188 $crate::write_slice(&v);
189 0
190 }
191 Err(v) => {
192 $crate::write_slice(&v);
193 1
194 }
195 }
196 }};
197}
198
199#[macro_export]
200macro_rules! write_exit_create {
201 ($ident:expr) => {{
202 let (addr, b, i) = $ident;
203 if addr != [0u8; 20] {
204 $crate::write_slice(&leftpad_addr(addr));
205 0
206 } else {
207 $crate::write_slice(&b[..i]);
208 1
209 }
210 }};
211}
212
213#[macro_export]
214macro_rules! write_exit_call {
215 ($ident:expr) => {{
216 let (rc, l, v) = $ident;
217 $crate::write_slice(&v[..l]);
218 if rc { 0 } else { 1 }
219 }};
220}
221
222pub fn read_args<const CAP: usize>(len: usize) -> ([u8; CAP], usize) {
226 assert!(CAP >= len, "cap not enough");
227 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
230 let mut b = unsafe { core::mem::MaybeUninit::<[u8; CAP]>::uninit().assume_init() };
231 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
233 let mut b = [0u8; CAP];
234 unsafe { host::read_args(b.as_mut_ptr()) };
235 (b, len)
236}
237
238#[cfg(all(target_arch = "riscv32", target_os = "none"))]
239pub fn args_len() -> usize {
240 unsafe { host::args_len() }
241}
242
243pub fn read_cd_res<W: EvmCdDeserialise>(len: usize) -> Result<W, EvmCdError> {
246 let mut args = W::new_buffer(len)?;
247 unsafe { host::read_args(args.as_mut().as_mut_ptr()) };
248 W::deserialise(&args.as_ref()[..len])
249}
250
251pub fn read_cd<W: EvmCdDeserialise>(len: usize) -> W {
253 read_cd_res::<W>(len).unwrap()
254}
255
256#[macro_export]
257macro_rules! read_args_safe {
258 ($len:expr, $max_len:expr) => {{
259 core::assert!($max_len >= $len, "{} < {}", $max_len, $len);
260 $crate::read_args::<$max_len>($len).0
261 }};
262}
263
264#[cfg(feature = "alloc")]
265pub fn read_args_vec(len: usize) -> Vec<u8> {
266 let mut b = Vec::with_capacity(len);
267 unsafe {
268 host::read_args(b.as_mut_ptr());
269 b.set_len(len);
270 };
271 b
272}
273
274pub fn msg_sender() -> Address {
275 let mut b = [0u8; 20];
276 unsafe { host::msg_sender(b.as_mut_ptr()) }
277 b
278}
279
280pub fn contract_address() -> Address {
281 let mut b = [0u8; 20];
282 unsafe { host::contract_address(b.as_mut_ptr()) }
283 b
284}
285
286pub fn msg_value() -> U {
287 let mut b = [0u8; 32];
288 unsafe { host::msg_value(b.as_mut_ptr()) }
289 U(b)
290}
291
292pub fn code_size(addr: Address) -> usize {
293 unsafe { host::account_code_size(addr.as_ptr()) }
294}
295
296pub fn code_slice<const CAP: usize>(
297 addr: Address,
298 size: usize,
299 offset: usize,
300) -> ([u8; CAP], usize) {
301 let mut b = [0u8; CAP];
302 assert!(CAP >= size, "not enough size: {size}, capacity: {CAP}");
303 let rd = unsafe { host::account_code(addr.as_ptr(), offset, size, b.as_mut_ptr()) };
304 (b, rd)
305}
306
307#[cfg(feature = "alloc")]
308pub fn code_vec_size(addr: Address, offset: usize, size: usize) -> Vec<u8> {
309 let mut b = Vec::with_capacity(size);
310 let rd = unsafe { host::account_code(addr.as_ptr(), offset, size, b.as_mut_ptr()) };
311 unsafe { b.set_len(rd) };
312 b
313}
314
315#[cfg(feature = "alloc")]
316pub fn code_vec(addr: Address, offset: usize) -> Vec<u8> {
317 code_vec_size(addr, offset, code_size(addr))
318}
319
320pub fn code_hash(addr: Address) -> U {
321 let mut b = U::ZERO;
322 unsafe { host::account_codehash(addr.as_ptr(), b.as_mut_ptr()) };
323 b
324}
325
326pub fn chain_id() -> u64 {
327 unsafe { host::chainid() }
328}
329
330pub fn block_timestamp() -> u64 {
331 unsafe { host::block_timestamp() }
332}
333
334pub fn block_basefee() -> U {
335 let mut out = U::ZERO;
336 unsafe { host::block_basefee(out.as_mut_ptr()) }
337 out
338}
339
340pub fn evm_gas_left() -> u64 {
341 unsafe { host::evm_gas_left() }
342}
343
344pub fn evm_ink_left() -> u64 {
345 unsafe { host::evm_ink_left() }
346}
347
348pub unsafe fn exit_early(code: usize) -> ! {
349 unsafe { host::exit_early(code as i32) }
350}
351
352pub unsafe fn storage_flush_cache(clear: bool) {
353 unsafe { host::storage_flush_cache(clear) }
354}
355
356pub fn msg_reentrant() -> bool {
358 unsafe { host::msg_reentrant() }
359}
360
361pub fn reentrancy_guard<R, F: FnOnce() -> R>(f: F) -> R {
364 assert!(!msg_reentrant(), "reentrancy is not allowed");
365 f()
366}
367
368#[cfg(all(test, feature = "std"))]
369mod tests {
370 use super::*;
371
372 #[test]
373 fn read_cd_uses_the_deserialiser_buffer() {
374 let mut args = vec![0u8; 32];
375 args[28..].copy_from_slice(&7u32.to_be_bytes());
376 host::set_args(args);
377
378 assert_eq!(read_cd_res::<u32>(32).unwrap(), 7);
379 assert!(read_cd_res::<u32>(33).is_err());
380 }
381}