near_helper/
lib.rs

1//! # NEAR-SDK-RS 4.0 pre-release
2//! 
3//! Some helper for NEAR 4.0 after upgrading from previous
4//! versions. 
5
6// use near_sdk::{env, require, utils};
7use std::collections::HashMap;
8
9pub mod timestamp;
10
11pub use crate::timestamp::*;
12
13/// Checks for successful promise. 
14#[deprecated(
15  since="0.2.0", 
16  note="please use near_sdk::utils::is_promise_success"
17)]
18pub fn is_promise_success(){
19    // utils::is_promise_success()
20    // false
21}
22
23
24/// The equivalent of .expect() but a lightweight version
25/// to reduce compiled-wasm size. 
26#[deprecated(
27  since="0.3.0",
28  note="just do unwrap_or_else(|| env::panic_str(...)) manually."
29)]
30pub fn expect_lightweight<T>(
31  _option: Option<T>,
32  _message: &str,
33) {
34    // option.unwrap_or_else(|| env::panic_str(message))
35}
36
37
38/// Assert predecessor is current, very frequently used
39/// assertion. 
40/// Similar to near_sdk::utils::assert_self except you can
41/// enter a custom message for claribility. 
42#[deprecated(
43  since="0.3.0",
44  note="use near_sdk::utils::assert_self instead."
45)]
46pub fn assert_predecessor_is_current(_message: &str) {
47    // require!(
48    //   env::predecessor_account_id() == env::current_account_id(),
49    //   message
50    // )
51}
52
53
54/// Converts yoctoNEAR to NEAR. 
55/// If there are many decimal places, will only leave the "first 5 decimals."
56/// 
57/// Note since String indexing isn't available, if you misuse it with inserting
58/// random non-ASCII value or with non-numbers, it'll panic/fail. 
59/// 
60/// And since we return f64, if your value is outside this range (very small
61/// number like 1000 yoctoNEAR), it won't panic, but there might be floating
62/// point errors which this function is not capable of detecting. 
63/// 
64/// FINAL NOTE: Because we need to index till 4, so if you have less than 4 figures,
65/// like 9999 yoctonear, it'll fail. If you have 10000, it'll pass (provided)
66/// it's within f64 range and ignore floating point errors. 
67/// 
68/// Example: 
69/// ```
70/// // Approx 3.193 NEAR
71/// let amount: u128 = 3_193_264_587_249_763_651_824_729;
72/// 
73/// assert_eq!(
74///   near_helper::yoctonear_to_near(amount),
75///   "3.19326".to_owned()
76/// );
77/// ```
78/// 
79/// Then Example: 
80/// ```
81/// // Approx 0.0214 NEAR
82/// let amount: u128 = 21_409_258_000_000_000_000_000;
83/// 
84/// assert_eq!(
85///   near_helper::yoctonear_to_near(amount),
86///   "0.021409".to_owned()
87/// );
88/// ```
89pub fn yoctonear_to_near(amount: u128) -> String {
90    let decimals = 5;
91
92    let amount_str = amount.to_string();
93    let amount_bytes = amount_str.as_bytes();
94
95    let amount_len = amount_bytes.len();
96
97    let mut num: String = "".to_owned();
98    if amount_len <= 24 {  // below 1 NEAR, which has len = 25
99      num.push_str("0.");
100
101      let append_zeros = 24 - amount_len;
102      for _ in 0..append_zeros {
103        num.push_str("0")
104      }
105
106      for i in 0..decimals {
107        if i < amount_len {
108          num.push(amount_bytes[i] as char)
109        }
110      }
111
112    } else {  // above 1 NEAR
113      let left = amount_len - 24;
114      
115      for i in 0..left {
116        num.push(amount_bytes[i] as char)
117      }
118      
119      num.push_str(".");
120
121      for i in left..left+decimals {
122        num.push(amount_bytes[i] as char)
123      }
124    }
125
126    num = num.trim_end_matches('0').to_owned();
127    num = num.trim_end_matches('.').to_owned();
128
129    num
130}
131
132
133/// NEAR to yoctonear conversion. 
134/// 
135/// Example:
136/// ```
137/// let amount = "3.214".to_owned();
138/// 
139/// assert_eq!(
140///   near_helper::near_to_yoctonear(amount),
141///   3_214_000_000_000_000_000_000_000u128
142/// );
143/// ```
144/// 
145/// 
146/// Will fail if somehow you insert a value less than 1 yoctoNEAR. 
147pub fn near_to_yoctonear(amount: String) -> u128 {
148    if amount == "0" { return 0; }
149    // let amount_str = amount.to_string();
150    let amount_str = amount;
151    let amount_bytes = amount_str.as_bytes();
152    
153    let amount_len = amount_bytes.len();
154
155    let mut num: String = "".to_owned();
156
157    if (amount_bytes[0] as char == '0') && (amount_bytes[1] as char == '.') {
158      let mut count = 0;
159
160      while count < amount_len {
161        if amount_bytes[count + 2] as char == '0' {
162          count += 1;
163        } else {
164          break
165        }
166      }
167
168      for i in count+2..amount_len {
169        num.push(amount_bytes[i] as char)
170      }
171
172      let actual_length = 24 - count;
173
174      for _ in num.len()..actual_length {
175        num.push_str("0")
176      }
177
178    } else {
179      let mut count = 0;
180
181      while count < amount_len {
182        if amount_bytes[count] as char != '.' {
183          count += 1
184        } else {
185          break
186        }
187      }
188
189      // left of decimal
190      for i in 0..count {
191        num.push(amount_bytes[i] as char)
192      }
193
194      // right of decimal
195      let remnant_num = amount_len - count;
196      let zeros_to_add = 25 - remnant_num;
197
198      for i in count+1..count+remnant_num {
199        num.push(amount_bytes[i] as char)
200      }
201
202      for _ in 0..zeros_to_add {
203        num.push_str("0")
204      }
205    }
206
207    num.parse().unwrap()
208}
209
210
211
212/// Return the scientific notation of a given value, 
213/// based on the digit you want to keep. This offers 
214/// you to calculate using the number of kept digits,
215/// hence simplifying calculations. 
216/// 
217/// This is useful if you want to keep calculation simple. 
218/// (UNDER TESTING CURRENTLY FOR PROVE.) Instead of 
219/// calculating with u128, use u32. 
220/// 
221/// CAVEAT: You lose accuracy depending on how much
222/// digit you choose to keep. All digits that you
223/// don't keep will be replaced by "0". Example: 
224/// if you keep 3 digits, and your value is 1.234x10^25, 
225/// it would return (123, 23) as it now moves to 123x10^23
226/// equivalent to 1.23x10^25. 
227/// 
228/// ```
229/// let amount: u128 = 2_913_464_000_000_000_000;
230/// 
231/// assert_eq!(
232///   near_helper::as_scientific_notation(amount, 3),
233///   (291u32, 16u8)
234/// );
235/// ```
236pub fn as_scientific_notation(
237  amount: u128,
238  keep_digit: usize
239) -> (u32, u8) {
240    let amount: String = amount.to_string();
241    let amount_bytes = amount.as_bytes();
242    let amount_len = amount_bytes.len();
243
244    let mut return_digit: String = "".to_owned();
245    for i in 0..keep_digit {
246      return_digit.push(amount_bytes[i] as char);
247    }
248
249    let power_of: u8 = amount_len as u8 - keep_digit as u8;
250    (return_digit.parse().unwrap(), power_of)
251}
252
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257    const ONE_NEAR: u128 = 1_000_000_000_000_000_000_000_000;
258
259    #[test]
260    fn test_yoctonear_to_near_conversion_correct_below_decimals() {
261      assert_eq!(yoctonear_to_near(ONE_NEAR / 500), "0.002".to_owned());
262    }
263
264    #[test]
265    fn test_yoctonear_conversion_correct_above_decimals() {
266      assert_eq!(yoctonear_to_near(ONE_NEAR * 12), "12".to_owned());
267    }
268
269
270    // #[test]
271    // fn test_yoctonear_conversion_too_small() {
272    //   // assert_eq!(yoctonear_to_near(10000), "0".to_owned());
273    // }
274
275    #[test]
276    fn test_yoctonear_zero_conversion_success() {
277      assert_eq!(yoctonear_to_near(0), "0".to_owned());
278    }
279
280    #[test]
281    fn test_near_zero_conversion_success() {
282      assert_eq!(near_to_yoctonear("0".to_owned()), 0);
283    }
284
285
286    #[test]
287    fn test_near_to_yoctonear_correct_less_than_one_near() {
288      assert_eq!(near_to_yoctonear("0.0021489".to_owned()), 2_148_900_000_000_000_000_000);
289    }
290
291
292    #[test]
293    fn test_near_to_yoctonear_correct_more_than_one_near() {
294      assert_eq!(near_to_yoctonear("127.864".to_owned()), 127_864_000_000_000_000_000_000_000);
295    }
296
297    // =======================================
298    // Chrono
299    fn datetime_comparer(datetime: HashMap<&'static str, String>, 
300      year: &str, month: &str, day: &str, hour: &str, mins: &str, 
301      secs: &str
302    ) {
303      println!("{:?}", datetime);
304      assert_eq!(datetime.get("year").unwrap().clone(), year.to_owned(), "year wrong.");
305      assert_eq!(datetime.get("month").unwrap().clone(), month.to_owned(), "month wrong.");
306      assert_eq!(datetime.get("day").unwrap().clone(), day.to_owned(), "day wrong.");
307      assert_eq!(datetime.get("hour").unwrap().clone(), hour.to_owned(), "hour wrong.");
308      assert_eq!(datetime.get("min").unwrap().clone(), mins.to_owned(), "min wrong.");
309      assert_eq!(datetime.get("sec").unwrap().clone(), secs.to_owned(), "sec wrong.");
310    }
311
312    #[test]
313    fn test_datetime_1() {
314      datetime_comparer(
315        timestamp_millis_to_datetime(388453887000),
316        "1982", "4", "23", "23", "51", "27"
317      );
318    }
319
320    #[test]
321    fn test_datetime_2() {
322      datetime_comparer(
323        timestamp_millis_to_datetime(0),
324        "1970", "1", "1", "0", "0", "0"
325      );
326    }
327
328    #[test]
329    fn test_datetime_3() {
330      datetime_comparer(
331        timestamp_millis_to_datetime(1704067202000),
332        "2024", "1", "1", "0", "0", "2"
333      );
334    }
335
336    #[test]
337    fn test_datetime_4_endtime() {
338      datetime_comparer(
339        timestamp_millis_to_datetime(1388534399000),
340        "2013", "12", "31", "23", "59", "59"
341      );
342    }
343
344    #[test]
345    fn test_datetime_5_leapfeb() {
346      datetime_comparer(
347        timestamp_millis_to_datetime(1709208000000),
348        "2024", "2", "29", "12", "0", "0"
349      );
350    }
351
352    #[test]
353    fn test_datetime_6() {
354      datetime_comparer(
355        timestamp_millis_to_datetime(1435649522000),
356        "2015", "6", "30", "7", "32", "2"
357      );
358    }
359
360    #[test]
361    fn test_datetime_7() {
362      datetime_comparer(
363        timestamp_millis_to_datetime(1409265002000),
364        "2014", "8", "28", "22", "30", "2"
365      );
366    }
367
368}