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 `gstd::msg` and `gstd::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        gstd::msg::id()
24    }
25
26    pub fn message_size() -> usize {
27        gstd::msg::size()
28    }
29
30    pub fn message_source() -> ActorId {
31        gstd::msg::source()
32    }
33
34    pub fn message_value() -> u128 {
35        gstd::msg::value()
36    }
37
38    pub fn reply_to() -> Result<MessageId, gcore::errors::Error> {
39        gstd::msg::reply_to()
40    }
41
42    pub fn reply_code() -> Result<ReplyCode, gcore::errors::Error> {
43        gstd::msg::reply_code()
44    }
45
46    #[cfg(not(feature = "ethexe"))]
47    pub fn signal_from() -> Result<MessageId, gcore::errors::Error> {
48        gstd::msg::signal_from()
49    }
50
51    #[cfg(not(feature = "ethexe"))]
52    pub fn signal_code() -> Result<Option<SignalCode>, gcore::errors::Error> {
53        gstd::msg::signal_code()
54    }
55
56    pub fn program_id() -> ActorId {
57        gstd::exec::program_id()
58    }
59
60    pub fn block_height() -> u32 {
61        gstd::exec::block_height()
62    }
63
64    pub fn block_timestamp() -> u64 {
65        gstd::exec::block_timestamp()
66    }
67
68    pub fn value_available() -> u128 {
69        gstd::exec::value_available()
70    }
71
72    pub fn env_vars() -> gstd::EnvVars {
73        gstd::exec::env_vars()
74    }
75
76    pub fn exit(inheritor_id: ActorId) -> ! {
77        gstd::exec::exit(inheritor_id)
78    }
79}
80
81#[cfg(not(target_arch = "wasm32"))]
82#[cfg(not(feature = "std"))]
83macro_rules! syscall_unimplemented {
84    ($($name:ident() -> $type:ty),* $(,)?) => {
85        impl Syscall {
86            $(
87                pub fn $name() -> $type {
88                    unimplemented!("{ERROR}")
89                }
90            )*
91        }
92    };
93}
94
95#[cfg(not(target_arch = "wasm32"))]
96#[cfg(not(feature = "std"))]
97const ERROR: &str = "Syscall is implemented only for the wasm32 architecture and the std future";
98
99#[cfg(not(target_arch = "wasm32"))]
100#[cfg(not(feature = "std"))]
101syscall_unimplemented!(
102    message_id() -> MessageId,
103    message_size() -> usize,
104    message_source() -> ActorId,
105    message_value() -> u128,
106    reply_to() -> Result<MessageId, gcore::errors::Error>,
107    reply_code() -> Result<ReplyCode, gcore::errors::Error>,
108    signal_from() -> Result<MessageId, gcore::errors::Error>,
109    signal_code() -> Result<Option<SignalCode>, gcore::errors::Error>,
110    program_id() -> ActorId,
111    block_height() -> u32,
112    block_timestamp() -> u64,
113    value_available() -> u128,
114    env_vars() -> gstd::EnvVars,
115);
116
117#[cfg(not(target_arch = "wasm32"))]
118#[cfg(not(feature = "std"))]
119impl Syscall {
120    pub fn exit(_inheritor_id: ActorId) -> ! {
121        unimplemented!("{ERROR}")
122    }
123}
124
125#[cfg(not(target_arch = "wasm32"))]
126#[cfg(feature = "std")]
127const _: () = {
128    use core::cell::RefCell;
129    use paste::paste;
130    use std::thread_local;
131
132    macro_rules! syscall_struct_impl {
133        ($($name:ident() -> $type:ty),* $(,)?) => {
134            #[derive(Clone)]
135            struct SyscallState {
136                $(
137                    $name: $type,
138                )*
139            }
140
141            thread_local! {
142                static SYSCALL_STATE: RefCell<SyscallState> = RefCell::new(SyscallState::default());
143            }
144
145            impl Syscall {
146                $(
147                    pub fn $name() -> $type {
148                        SYSCALL_STATE.with_borrow(|state| state.$name.clone())
149                    }
150                )*
151            }
152
153            impl Syscall {
154                $(
155                    paste! {
156                        pub fn [<with_ $name>]($name: $type) {
157                            SYSCALL_STATE.with_borrow_mut(|state| state.$name = $name);
158                        }
159                    }
160                )*
161            }
162        };
163    }
164
165    syscall_struct_impl!(
166        message_id() -> MessageId,
167        message_size() -> usize,
168        message_source() -> ActorId,
169        message_value() -> u128,
170        reply_to() -> Result<MessageId, gcore::errors::Error>,
171        reply_code() -> Result<ReplyCode, gcore::errors::Error>,
172        signal_from() -> Result<MessageId, gcore::errors::Error>,
173        signal_code() -> Result<Option<SignalCode>, gcore::errors::Error>,
174        program_id() -> ActorId,
175        block_height() -> u32,
176        block_timestamp() -> u64,
177        value_available() -> u128,
178    );
179
180    impl Default for SyscallState {
181        fn default() -> Self {
182            use gear_core_errors::{ExecutionError, ExtError};
183
184            Self {
185                message_id: MessageId::default(),
186                message_size: 0,
187                message_source: ActorId::default(),
188                message_value: 0,
189                reply_to: Err(ExtError::Execution(ExecutionError::NoReplyContext).into()),
190                reply_code: Err(ExtError::Execution(ExecutionError::NoReplyContext).into()),
191                signal_from: Err(ExtError::Execution(ExecutionError::NoSignalContext).into()),
192                signal_code: Err(ExtError::Execution(ExecutionError::NoSignalContext).into()),
193                program_id: ActorId::default(),
194                block_height: 0,
195                block_timestamp: 0,
196                value_available: 0,
197            }
198        }
199    }
200
201    impl Syscall {
202        pub fn env_vars() -> gstd::EnvVars {
203            gstd::EnvVars {
204                performance_multiplier: gstd::Percent::new(100),
205                existential_deposit: 1_000_000_000_000,
206                mailbox_threshold: 3000,
207                gas_multiplier: gstd::GasMultiplier::from_value_per_gas(100),
208            }
209        }
210
211        pub fn exit(inheritor_id: ActorId) -> ! {
212            panic!("Program exited with inheritor id: {}", inheritor_id);
213        }
214    }
215};