1#![no_std]
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[allow(unused)]
7use core::fmt::{Result as FmtResult, Write};
8
9use paste::paste;
10
11#[cfg(all(target_family = "wasm", target_os = "unknown"))]
12mod wasm {
13 #[link(wasm_import_module = "console")]
14 #[cfg(feature = "console")]
15 unsafe extern "C" {
16 pub(crate) fn log_txt(ptr: *const u8, len: usize);
17 }
18
19 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
20 #[link(wasm_import_module = "vm_hooks")]
21 #[allow(unused)]
22 unsafe extern "C" {
23 pub(crate) fn exit_early(code: i32) -> !;
24 pub(crate) fn write_result(d: *const u8, l: usize);
25 }
26}
27
28#[cfg(all(target_family = "wasm", target_os = "unknown"))]
29fn write_result_slice(s: &[u8]) {
30 unsafe { wasm::write_result(s.as_ptr(), s.len()) }
31}
32
33pub const PANIC_PREAMBLE_WORD: [u8; 32 + 4] = match const_hex::const_decode_to_array::<{ 32 + 4 }>(
35 b"4e487b710000000000000000000000000000000000000000000000000000000000000000",
36) {
37 Ok(v) => v,
38 Err(_) => panic!(),
39};
40
41pub const ERROR_PREAMBLE_OFFSET: [u8; 4 + 32] = match const_hex::const_decode_to_array::<{ 4 + 32 }>(
43 b"08c379a00000000000000000000000000000000000000000000000000000000000000020",
44) {
45 Ok(v) => v,
46 Err(_) => panic!(),
47};
48
49#[derive(Clone, Debug, PartialEq)]
50#[repr(u8)]
51pub enum PanicCodes {
52 OverflowOrUnderflow = 0x11,
53 DivByZero = 0x12,
54 DecodingError = 0x22,
55}
56
57#[cfg(all(target_family = "wasm", target_os = "unknown"))]
58pub fn panic_with_code(x: PanicCodes) -> ! {
59 let mut b = PANIC_PREAMBLE_WORD;
60 b[4 + 32 - 1] = x as u8;
61 write_result_slice(&b);
62 unsafe { wasm::exit_early(1) }
63}
64
65#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
66pub fn panic_with_code(x: PanicCodes) -> ! {
67 panic!("panicked with code: {x:?}");
68}
69
70#[macro_export]
71macro_rules! define_panic_macros {
72 (
73 $(($error_msg:expr, $panic_code:ident)),* $(,)?
74 ) => {
75 $(
76 paste! {
77 #[macro_export]
78 macro_rules! [<panic_on_err_ $error_msg>] {
79 ($e:expr, $msg:expr) => {{
80 match $e {
81 Some(v) => v,
82 None => {
83 #[cfg(feature = "msg-on-sdk-err")]
84 panic!("{}: {}", $error_msg, $msg);
85 #[cfg(not(feature = "msg-on-sdk-err"))]
86 $crate::panic_with_code($crate::PanicCodes::$panic_code);
87 }
88 }
89 }};
90 }
91 }
92 )*
93 };
94}
95
96define_panic_macros!(
97 ("overflow", OverflowOrUnderflow),
98 ("div_by_zero", DivByZero),
99);
100
101#[macro_export]
102macro_rules! panic_on_err_bad_decoding_bool {
103 ($msg:expr) => {{
104 #[cfg(feature = "msg-on-sdk-err")]
105 panic!("error decoding: {}", $msg);
106 #[cfg(not(feature = "msg-on-sdk-err"))]
107 $crate::panic_with_code($crate::PanicCodes::DecodingError);
108 }};
109 ($e:expr, $msg:expr) => {{
110 if !$e {
111 panic_on_err_bad_decoding_bool!($msg);
112 }
113 }};
114}
115
116#[allow(unused)]
117struct SliceWriter<'a>(&'a mut [u8], usize);
118
119impl<'a> Write for SliceWriter<'a> {
120 fn write_str(&mut self, s: &str) -> FmtResult {
121 let v = s.len().min(REVERT_BUF_SIZE.saturating_sub(self.1));
122 self.0[self.1..self.1 + v].copy_from_slice(&s.as_bytes()[..v]);
123 self.1 += v;
124 Ok(())
125 }
126}
127
128#[allow(unused)]
132const REVERT_BUF_SIZE: usize = 1024;
133
134#[cfg(all(feature = "panic-revert", feature = "panic-loc"))]
135compile_error!("panic-revert and panic-loc simultaneously enabled");
136
137#[cfg(all(target_family = "wasm", target_os = "unknown"))]
138#[cfg_attr(all(feature = "panic", not(feature = "std")), panic_handler)]
139pub fn panic_handler(_msg: &core::panic::PanicInfo) -> ! {
140 #[cfg(feature = "console")]
141 {
142 let msg = alloc::format!("{_msg}");
143 unsafe { wasm::log_txt(msg.as_ptr(), msg.len()) }
144 }
145 #[cfg(any(feature = "panic-revert", feature = "panic-loc"))]
146 {
147 let mut buf = [0u8; REVERT_BUF_SIZE];
148 buf[..ERROR_PREAMBLE_OFFSET.len()].copy_from_slice(&ERROR_PREAMBLE_OFFSET);
149 let mut w = SliceWriter(&mut buf[ERROR_PREAMBLE_OFFSET.len() + 32..], 0);
150 #[cfg(feature = "panic-revert")]
151 {
152 write!(&mut w, "{_msg}").unwrap();
153 }
154 #[cfg(feature = "panic-loc")]
155 if let Some(loc) = _msg.location() {
156 write!(&mut w, "panic: {}:{}", loc.file(), loc.line()).unwrap();
157 } else {
158 write!(&mut w, "panic: unknown").unwrap();
159 }
160 let len_msg = w.1;
161 let len_offset = ERROR_PREAMBLE_OFFSET.len();
162 buf[len_offset + 28..len_offset + 32].copy_from_slice(&(len_msg as u32).to_be_bytes());
163 let len_full = ERROR_PREAMBLE_OFFSET.len() + 32 + len_msg;
164 let len_padded = len_full + (32 - (len_full % 32)) % 32;
165 write_result_slice(&buf[..len_padded]);
166 unsafe { wasm::exit_early(1) }
167 }
168 #[allow(unreachable_code)]
172 core::arch::wasm32::unreachable()
173}