near_sdk/utils/mod.rs
1//! Helper methods that often used in smart contracts.
2
3pub(crate) mod storage_key_impl;
4
5mod stable_map;
6pub(crate) use self::stable_map::StableMap;
7mod cache_entry;
8pub(crate) use cache_entry::{CacheEntry, EntryState};
9
10use crate::{env, NearToken, PromiseResult};
11
12/// Helper macro to log a message through [`env::log_str`].
13/// This macro can be used similar to the [`std::format`] macro.
14///
15/// This differs from [`std::format`] because instead of generating a string, it will log the utf8
16/// bytes as a log through [`env::log_str`].
17///
18/// The logged message will get persisted on chain.
19///
20/// # Example use
21///
22/// ```no_run
23/// use near_sdk::log;
24///
25/// # fn main() {
26/// log!("test");
27/// let world: &str = "world";
28/// log!("{world}");
29/// log!("Hello {}", world);
30/// log!("x = {}, y = {y}", 10, y = 30);
31/// # }
32/// ```
33///
34/// [`env::log_str`]: crate::env::log_str
35#[macro_export]
36macro_rules! log {
37 ($($arg:tt)*) => {
38 $crate::env::log_str(::std::format!($($arg)*).as_str())
39 };
40}
41
42/// Helper macro to create assertions that will panic through the runtime host functions.
43///
44/// This macro can be used similarly to [`assert!`] but will reduce code size by not including
45/// file and rust specific data in the panic message.
46///
47/// # Examples
48///
49/// ```no_run
50/// use near_sdk::require;
51///
52/// # fn main() {
53/// let a = 2;
54/// require!(a > 0);
55/// require!("test" != "other", "Some custom error message if false");
56/// # }
57/// ```
58#[macro_export]
59macro_rules! require {
60 ($cond:expr $(,)?) => {
61 if cfg!(debug_assertions) {
62 assert!($cond)
63 } else if !$cond {
64 $crate::env::panic_str("require! assertion failed");
65 }
66 };
67 ($cond:expr, $message:expr $(,)?) => {
68 if cfg!(debug_assertions) {
69 // Error message must be &str to match panic_str signature
70 let msg: &str = &$message;
71 assert!($cond, "{}", msg)
72 } else if !$cond {
73 $crate::env::panic_str(&$message)
74 }
75 };
76}
77
78/// Assert that predecessor_account_id == current_account_id, meaning contract called itself.
79pub fn assert_self() {
80 require!(env::predecessor_account_id() == env::current_account_id(), "Method is private");
81}
82
83/// Assert that 1 yoctoNEAR was attached.
84pub fn assert_one_yocto() {
85 require!(
86 env::attached_deposit() == NearToken::from_yoctonear(1),
87 "Requires attached deposit of exactly 1 yoctoNEAR"
88 )
89}
90
91/// Returns true if promise was successful.
92///
93/// Calls [`crate::env::panic_str`] **host function** if called outside a callback that received precisely 1 promise result.
94///
95/// Uses low-level [`crate::env::promise_results_count`] and [`crate::env::promise_result`] **host functions**.
96pub fn is_promise_success() -> bool {
97 require!(
98 env::promise_results_count() == 1,
99 "Contract expected a single result on the callback"
100 );
101 env::promise_result_internal(0).is_ok()
102}
103
104/// Returns the result of the promise if successful. Otherwise returns None.
105///
106/// Calls [`crate::env::panic_str`] **host function** if called outside a callback that received precisely 1 promise result.
107///
108/// Uses low-level [`crate::env::promise_results_count`] and [`crate::env::promise_result`] **host functions**.
109pub fn promise_result_as_success() -> Option<Vec<u8>> {
110 require!(
111 env::promise_results_count() == 1,
112 "Contract expected a single result on the callback"
113 );
114 match env::promise_result(0) {
115 PromiseResult::Successful(result) => Some(result),
116 _ => None,
117 }
118}
119
120/// Deprecated helper function which used to generate code to initialize the [`GlobalAllocator`].
121/// This is now initialized by default. Disable `wee_alloc` feature to configure manually.
122///
123/// [`GlobalAllocator`]: std::alloc::GlobalAlloc
124#[deprecated(
125 since = "4.0.0",
126 note = "Allocator is already initialized with the default `wee_alloc` feature set. \
127 Please make sure you don't disable default features on the SDK or set the global \
128 allocator manually."
129)]
130#[macro_export]
131macro_rules! setup_alloc {
132 () => {};
133}
134
135#[cfg(test)]
136mod tests {
137 use crate::test_utils::get_logs;
138
139 #[test]
140 fn test_log_simple() {
141 log!("hello");
142
143 assert_eq!(get_logs(), vec!["hello".to_string()]);
144 }
145
146 #[test]
147 fn test_log_format() {
148 log!("hello {} ({})", "user_name", 25);
149
150 assert_eq!(get_logs(), vec!["hello user_name (25)".to_string()]);
151 }
152}