Skip to main content

sails_rs/gstd/
syscalls.rs

1use crate::prelude::*;
2
3/// System call interface for accessing the runtime environment.
4///
5/// The `Syscall` struct provides a collection of methods that abstract lower-level operations,
6/// such as retrieving message metadata (ID, size, source, value), fetching the program identifier,
7/// obtaining the current block height, and accessing environment variables.
8///
9/// These methods are essential for enabling on-chain applications to interact with the Gear runtime
10/// in a consistent manner. Depending on the target environment, different implementations are provided:
11///
12/// - For the WASM target, direct calls are made to `gcore::msg` and `gcore::exec` to fetch runtime data.
13/// - In standard (`std`) environments, a mock implementation uses thread-local state for testing purposes.
14/// - In `no_std` configurations without the `std` feature and not WASM target, the functions are marked as unimplemented.
15///
16/// Use these methods to retrieve contextual information about the current execution environment,
17/// ensuring that your program logic remains agnostic of the underlying platform specifics.
18pub struct Syscall;
19
20#[cfg(target_arch = "wasm32")]
21impl Syscall {
22    pub fn message_id() -> MessageId {
23        ::gcore::msg::id()
24    }
25
26    pub fn message_size() -> usize {
27        ::gcore::msg::size()
28    }
29
30    pub fn message_source() -> ActorId {
31        ::gcore::msg::source()
32    }
33
34    pub fn message_value() -> ValueUnit {
35        ::gcore::msg::value()
36    }
37
38    pub fn reply_to() -> Result<MessageId, gcore::errors::Error> {
39        ::gcore::msg::reply_to()
40    }
41
42    pub fn reply_code() -> Result<gcore::errors::ReplyCode, gcore::errors::Error> {
43        ::gcore::msg::reply_code()
44    }
45
46    #[cfg(not(feature = "ethexe"))]
47    pub fn signal_from() -> Result<MessageId, gcore::errors::Error> {
48        ::gcore::msg::signal_from()
49    }
50
51    #[cfg(not(feature = "ethexe"))]
52    pub fn signal_code() -> Result<Option<::gcore::errors::SignalCode>, gcore::errors::Error> {
53        ::gcore::msg::signal_code()
54    }
55
56    pub fn program_id() -> ActorId {
57        ::gcore::exec::program_id()
58    }
59
60    pub fn block_height() -> u32 {
61        ::gcore::exec::block_height()
62    }
63
64    pub fn block_timestamp() -> u64 {
65        ::gcore::exec::block_timestamp()
66    }
67
68    pub fn value_available() -> ValueUnit {
69        ::gcore::exec::value_available()
70    }
71
72    pub fn gas_available() -> GasUnit {
73        ::gcore::exec::gas_available()
74    }
75
76    pub fn env_vars() -> ::gcore::EnvVars {
77        ::gcore::exec::env_vars()
78    }
79
80    pub fn exit(inheritor_id: ActorId) -> ! {
81        ::gcore::exec::exit(inheritor_id)
82    }
83
84    pub fn panic(data: &[u8]) -> ! {
85        ::gcore::ext::panic(data)
86    }
87
88    pub fn read_bytes() -> Result<Vec<u8>, ::gcore::errors::Error> {
89        let mut result = vec![0u8; ::gcore::msg::size()];
90        ::gcore::msg::read(result.as_mut())?;
91        Ok(result)
92    }
93
94    #[cfg(not(feature = "ethexe"))]
95    pub fn system_reserve_gas(amount: GasUnit) -> Result<(), ::gcore::errors::Error> {
96        ::gcore::exec::system_reserve_gas(amount)
97    }
98}
99
100#[cfg(not(target_arch = "wasm32"))]
101#[cfg(not(feature = "std"))]
102macro_rules! syscall_unimplemented {
103    ($($name:ident(  $( $param:ident : $ty:ty ),* ) -> $type:ty),* $(,)?) => {
104        impl Syscall {
105            $(
106                pub fn $name($( $param: $ty ),* ) -> $type {
107                    unimplemented!("{ERROR}")
108                }
109            )*
110        }
111    };
112}
113
114#[cfg(not(target_arch = "wasm32"))]
115#[cfg(not(feature = "std"))]
116const ERROR: &str = "Syscall is implemented only for the wasm32 architecture and the std future";
117
118#[cfg(not(target_arch = "wasm32"))]
119#[cfg(not(feature = "std"))]
120syscall_unimplemented!(
121    message_id() -> MessageId,
122    message_size() -> usize,
123    message_source() -> ActorId,
124    message_value() -> ValueUnit,
125    reply_to() -> Result<MessageId, gcore::errors::Error>,
126    reply_code() -> Result<ReplyCode, gcore::errors::Error>,
127    signal_from() -> Result<MessageId, gcore::errors::Error>,
128    signal_code() -> Result<Option<SignalCode>, gcore::errors::Error>,
129    program_id() -> ActorId,
130    block_height() -> u32,
131    block_timestamp() -> u64,
132    value_available() -> ValueUnit,
133    gas_available() -> GasUnit,
134    env_vars() -> ::gcore::EnvVars,
135    exit(_inheritor_id: ActorId) -> !,
136    panic(_data: &[u8]) -> !,
137    read_bytes() -> Result<Vec<u8>, gcore::errors::Error>,
138    system_reserve_gas(_amount: GasUnit) -> Result<(), ::gcore::errors::Error>,
139);
140
141#[cfg(not(target_arch = "wasm32"))]
142#[cfg(feature = "std")]
143const _: () = {
144    use core::cell::RefCell;
145    use paste::paste;
146    use std::thread_local;
147
148    macro_rules! syscall_struct_impl {
149        ($($name:ident() -> $type:ty),* $(,)?) => {
150            #[derive(Clone)]
151            struct SyscallState {
152                $(
153                    $name: $type,
154                )*
155            }
156
157            thread_local! {
158                static SYSCALL_STATE: RefCell<SyscallState> = RefCell::new(SyscallState::default());
159            }
160
161            impl Syscall {
162                $(
163                    pub fn $name() -> $type {
164                        SYSCALL_STATE.with_borrow(|state| state.$name.clone())
165                    }
166                )*
167            }
168
169            impl Syscall {
170                $(
171                    paste! {
172                        pub fn [<with_ $name>]($name: $type) {
173                            SYSCALL_STATE.with_borrow_mut(|state| state.$name = $name);
174                        }
175                    }
176                )*
177            }
178        };
179    }
180
181    syscall_struct_impl!(
182        message_id() -> MessageId,
183        message_size() -> usize,
184        message_source() -> ActorId,
185        message_value() -> ValueUnit,
186        reply_to() -> Result<MessageId, gcore::errors::Error>,
187        reply_code() -> Result<ReplyCode, gcore::errors::Error>,
188        signal_from() -> Result<MessageId, gcore::errors::Error>,
189        signal_code() -> Result<Option<SignalCode>, gcore::errors::Error>,
190        program_id() -> ActorId,
191        block_height() -> u32,
192        block_timestamp() -> u64,
193        value_available() -> ValueUnit,
194        gas_available() -> GasUnit,
195        read_bytes() -> Result<Vec<u8>, gcore::errors::Error>,
196    );
197
198    impl Default for SyscallState {
199        fn default() -> Self {
200            use gear_core_errors::{ExecutionError, ExtError};
201
202            Self {
203                message_id: MessageId::default(),
204                message_size: 0,
205                message_source: ActorId::default(),
206                message_value: 0,
207                reply_to: Err(ExtError::Execution(ExecutionError::NoReplyContext).into()),
208                reply_code: Err(ExtError::Execution(ExecutionError::NoReplyContext).into()),
209                signal_from: Err(ExtError::Execution(ExecutionError::NoSignalContext).into()),
210                signal_code: Err(ExtError::Execution(ExecutionError::NoSignalContext).into()),
211                program_id: ActorId::default(),
212                block_height: 0,
213                block_timestamp: 0,
214                value_available: 0,
215                gas_available: 0,
216                read_bytes: Err(::gcore::errors::Error::SyscallUsage),
217            }
218        }
219    }
220
221    impl Syscall {
222        pub fn env_vars() -> ::gcore::EnvVars {
223            ::gcore::EnvVars {
224                performance_multiplier: ::gcore::Percent::new(100),
225                existential_deposit: 1_000_000_000_000,
226                mailbox_threshold: 3000,
227                gas_multiplier: ::gcore::GasMultiplier::from_value_per_gas(100),
228            }
229        }
230
231        pub fn exit(inheritor_id: ActorId) -> ! {
232            panic!("Program exited with inheritor id: {}", inheritor_id);
233        }
234
235        pub fn panic(data: &[u8]) -> ! {
236            if data.starts_with(b"GM") && data.len() >= 16 {
237                let mut payload = &data[16..];
238                if let Ok(s) = <String as parity_scale_codec::Decode>::decode(&mut payload) {
239                    panic!("{}", s);
240                }
241            }
242            panic!("{:?}", data);
243        }
244
245        #[cfg(not(feature = "ethexe"))]
246        pub fn system_reserve_gas(_amount: GasUnit) -> Result<(), ::gcore::errors::Error> {
247            Ok(())
248        }
249    }
250};