Skip to main content

bobcat_entry/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(feature = "alloc")]
7use alloc::vec::Vec;
8
9pub use bobcat_maths::U;
10
11type Address = [u8; 20];
12
13pub use bobcat_cd::read_words;
14
15pub use bobcat_host as host;
16
17pub fn balance(addr: Address) -> U {
18    let mut out = U::ZERO;
19    unsafe { host::account_balance(addr.as_ptr(), out.as_mut_ptr()) }
20    out
21}
22
23#[unsafe(no_mangle)]
24#[cfg(all(
25    target_family = "wasm",
26    target_os = "unknown",
27    not(feature = "dont-define-symbols")
28))]
29pub unsafe fn mark_used() {
30    unsafe { host::pay_for_memory_grow(0) }
31    panic!();
32}
33
34/// Write a result array, avoiding a copy.
35pub fn write_result_arr<const CAP: usize>(s: [u8; CAP]) {
36    unsafe { host::write_result(s.as_ptr(), s.len()) }
37}
38
39/// Write a result slice.
40pub fn write_result_slice(s: &[u8]) {
41    unsafe { host::write_result(s.as_ptr(), s.len()) }
42}
43
44pub fn write_result_word(s: &U) {
45    write_result_slice(&s.0)
46}
47
48pub fn write_result_bool(v: bool) {
49    write_result_slice(&U::from(v).0)
50}
51
52pub fn return_data_size() -> usize {
53    unsafe { host::return_data_size() }
54}
55
56pub use bobcat_cd::leftpad_addr;
57
58/// Like write_result_exit_call, except it only reverts with the
59/// returndata if the underlying call reverted. If it doesn't, then it
60/// just returns the slice.
61#[macro_export]
62macro_rules! revert_if_bad_call_vec {
63    ($e:expr) => {{
64        let (rc, rd) = $e;
65        if !rc {
66            $crate::write_result_slice(&rd);
67            return 1;
68        }
69        rd
70    }};
71}
72
73/// Reverts if the underlying call failed, using the vector that was
74/// returned as the third argument as slice.
75#[macro_export]
76macro_rules! revert_if_bad_call_unit_vec {
77    ($e:expr) => {{
78        let (rc, revertdata) = $e;
79        match (rc, revertdata) {
80            (true, _) => (),
81            (false, Some(v)) => {
82                $crate::write_result_slice(&v);
83                return 1;
84            }
85            (false, _) => return 1,
86        }
87    }};
88}
89
90/// Reverts with a message if the revertdata is Some, and if the rc is false.
91#[macro_export]
92macro_rules! revert_if_bad_call_slice_vec {
93    ($e:expr) => {{
94        let (rc, returndata, revertdata) = $e;
95        match (rc, revertdata) {
96            (true, _) => returndata,
97            (false, Some(v)) => {
98                $crate::write_result_slice(&v);
99                return 1;
100            }
101            (false, _) => return 1,
102        }
103    }};
104}
105
106#[macro_export]
107macro_rules! write_result_exit_res {
108    ($ident:expr) => {{
109        match $ident {
110            Ok(v) => {
111                $crate::write_result_slice(&v);
112                0
113            }
114            Err(v) => {
115                $crate::write_result_slice(&v);
116                1
117            }
118        }
119    }};
120}
121
122#[macro_export]
123macro_rules! write_result_exit_create {
124    ($ident:expr) => {{
125        let (addr, b, i) = $ident;
126        if addr != [0u8; 20] {
127            $crate::write_result_slice(&leftpad_addr(addr));
128            0
129        } else {
130            $crate::write_result_slice(&b[..i]);
131            1
132        }
133    }};
134}
135
136#[macro_export]
137macro_rules! write_result_exit_call {
138    ($ident:expr) => {{
139        let (rc, l, v) = $ident;
140        $crate::write_result_slice(&v[..l]);
141        if rc {
142            0
143        } else {
144            1
145        }
146    }};
147}
148
149/// Read arguments from the Stylus VM (calldata), using unsafe
150/// MaybeUninit code if we're on the wasm host. If we're on a non wasm
151/// host, zero it out then return it. We also return the length.
152pub fn read_args<const CAP: usize>(len: usize) -> ([u8; CAP], usize) {
153    assert!(CAP >= len, "cap not enough");
154    // SAFETY: This is safe since the host will write over this, and on
155    // the Stylus host this will always be zeroed out anyway.
156    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
157    let mut b = unsafe { core::mem::MaybeUninit::<[u8; CAP]>::uninit().assume_init() };
158    // On other hosts, we don't make the assumption about this and zero it out:
159    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
160    let mut b = [0u8; CAP];
161    unsafe { host::read_args(b.as_mut_ptr()) };
162    (b, len)
163}
164
165#[cfg(all(target_arch = "riscv32", target_os = "none"))]
166pub fn args_len() -> usize {
167    unsafe { host::args_len() }
168}
169
170#[macro_export]
171macro_rules! read_args_safe {
172    ($len:expr, $max_len:expr) => {{
173        core::assert!($max_len >= $len, "{} < {}", $max_len, $len);
174        $crate::read_args::<$max_len>($len).0
175    }};
176}
177
178#[cfg(feature = "alloc")]
179pub fn read_args_vec(len: usize) -> Vec<u8> {
180    let mut b = Vec::with_capacity(len);
181    unsafe {
182        host::read_args(b.as_mut_ptr());
183        b.set_len(len);
184    };
185    b
186}
187
188pub fn msg_sender() -> Address {
189    let mut b = [0u8; 20];
190    unsafe { host::msg_sender(b.as_mut_ptr()) }
191    b
192}
193
194pub fn contract_address() -> Address {
195    let mut b = [0u8; 20];
196    unsafe { host::contract_address(b.as_mut_ptr()) }
197    b
198}
199
200pub fn msg_value() -> U {
201    let mut b = [0u8; 32];
202    unsafe { host::msg_value(b.as_mut_ptr()) }
203    U(b)
204}
205
206pub fn code_size(addr: Address) -> usize {
207    unsafe { host::account_code_size(addr.as_ptr()) }
208}
209
210pub fn code_slice<const CAP: usize>(
211    addr: Address,
212    size: usize,
213    offset: usize,
214) -> ([u8; CAP], usize) {
215    let mut b = [0u8; CAP];
216    assert!(CAP >= size, "not enough size: {size}, capacity: {CAP}");
217    let rd = unsafe { host::account_code(addr.as_ptr(), offset, size, b.as_mut_ptr()) };
218    (b, rd)
219}
220
221#[cfg(feature = "alloc")]
222pub fn code_vec_size(addr: Address, offset: usize, size: usize) -> Vec<u8> {
223    let mut b = Vec::with_capacity(size);
224    let rd = unsafe { host::account_code(addr.as_ptr(), offset, size, b.as_mut_ptr()) };
225    unsafe { b.set_len(rd) };
226    b
227}
228
229#[cfg(feature = "alloc")]
230pub fn code_vec(addr: Address, offset: usize) -> Vec<u8> {
231    code_vec_size(addr, offset, code_size(addr))
232}
233
234pub fn code_hash(addr: Address) -> U {
235    let mut b = U::ZERO;
236    unsafe { host::account_codehash(addr.as_ptr(), b.as_mut_ptr()) };
237    b
238}
239
240pub fn chain_id() -> u64 {
241    unsafe { host::chainid() }
242}
243
244pub fn block_timestamp() -> u64 {
245    unsafe { host::block_timestamp() }
246}
247
248pub fn block_basefee() -> U {
249    let mut out = U::ZERO;
250    unsafe { host::block_basefee(out.as_mut_ptr()) }
251    out
252}
253
254pub fn evm_gas_left() -> u64 {
255    unsafe { host::evm_gas_left() }
256}
257
258pub fn evm_ink_left() -> u64 {
259    unsafe { host::evm_ink_left() }
260}
261
262pub unsafe fn exit_early(code: usize) -> ! {
263    unsafe { host::exit_early(code as i32) }
264}
265
266pub unsafe fn storage_flush_cache(clear: bool) {
267    unsafe { host::storage_flush_cache(clear) }
268}