1use borsh::BorshSerialize;
2pub use l1x_sdk_macros::contract;
3pub use l1x_sys as sys;
4use std::panic as std_panic;
5use types::{Address, Balance, BlockHash, BlockNumber, Gas, TimeStamp};
6
7pub mod contract_interaction;
8pub mod store;
9pub mod types;
10use contract_interaction::ContractCall;
11pub mod utils;
12pub(crate) use crate::utils::*;
13
14const EVICTED_REGISTER: u64 = std::u64::MAX - 1;
15const ATOMIC_OP_REGISTER: u64 = std::u64::MAX - 2;
16
17#[derive(Debug)]
18pub enum TransferError {
19 TransferFailed,
20 InsufficientFunds,
21}
22
23macro_rules! try_method_into_register {
24 ( $method:ident ) => {{
25 unsafe { l1x_sys::$method(ATOMIC_OP_REGISTER) };
26 read_register(ATOMIC_OP_REGISTER)
27 }};
28}
29
30macro_rules! method_into_register {
31 ( $method:ident ) => {{
32 expect_register(try_method_into_register!($method))
33 }};
34}
35
36fn register_len(register_id: u64) -> Option<u64> {
38 let len = unsafe { l1x_sys::register_len(register_id) };
39 if len == std::u64::MAX {
40 None
41 } else {
42 Some(len)
43 }
44}
45
46fn read_register(register_id: u64) -> Option<Vec<u8>> {
48 let len: usize = register_len(register_id)?
49 .try_into()
50 .unwrap_or_else(|_| abort());
51
52 let mut buffer = Vec::with_capacity(len);
53
54 unsafe {
55 l1x_sys::read_register(register_id, buffer.as_mut_ptr() as u64);
56
57 buffer.set_len(len);
58 }
59 Some(buffer)
60}
61
62fn expect_register<T>(option: Option<T>) -> T {
63 option.unwrap_or_else(|| abort())
64}
65
66fn panic_hook_impl(info: &std_panic::PanicInfo) {
69 panic(&info.to_string());
70}
71
72pub fn setup_panic_hook() {
74 std_panic::set_hook(Box::new(panic_hook_impl));
75}
76
77pub fn abort() -> ! {
80 #[cfg(test)]
81 std::panic!("Mocked panic function called!");
82 #[cfg(not(test))]
83 unsafe {
84 l1x_sys::panic()
85 }
86}
87
88pub fn panic(message: &str) -> ! {
90 msg(message);
91
92 #[cfg(test)]
93 std::panic!("Mocked panic function called!");
94 #[cfg(not(test))]
95 unsafe {
96 l1x_sys::panic_msg(message.as_ptr() as _, message.len() as _)
97 }
98}
99
100pub fn input() -> Option<Vec<u8>> {
102 #[cfg(test)]
103 {
104 return tests::input();
105 }
106 #[cfg(not(test))]
107 try_method_into_register!(input)
108}
109
110pub fn output(data: &[u8]) {
112 #[cfg(test)]
113 {
114 return tests::output(data);
115 }
116 #[cfg(not(test))]
117 unsafe {
118 sys::output(data.as_ptr() as _, data.len() as _)
119 }
120}
121
122pub fn msg(message: &str) {
123 #[cfg(test)]
124 {
125 return tests::msg(message);
126 }
127 #[cfg(not(test))]
128 {
129 #[cfg(all(debug_assertions, not(target_arch = "wasm32")))]
130 eprintln!("{}", message);
131
132 unsafe { l1x_sys::msg(message.as_ptr() as _, message.len() as _) }
133 }
134}
135
136pub fn storage_write(key: &[u8], value: &[u8]) -> bool {
142 #[cfg(test)]
143 {
144 return tests::storage_write(key, value);
145 }
146 #[cfg(not(test))]
147 match unsafe {
148 sys::storage_write(
149 key.as_ptr() as _,
150 key.len() as _,
151 value.as_ptr() as _,
152 value.len() as _,
153 EVICTED_REGISTER,
154 )
155 } {
156 0 => false,
157 1 => true,
158 _ => abort(),
159 }
160}
161
162pub fn storage_remove(key: &[u8]) -> bool {
166 #[cfg(test)]
167 {
168 return tests::storage_remove(key);
169 }
170
171 #[cfg(not(test))]
172 match unsafe { sys::storage_remove(key.as_ptr() as _, key.len() as _, EVICTED_REGISTER) } {
173 0 => false,
174 1 => true,
175 _ => abort(),
176 }
177}
178
179pub fn storage_read(key: &[u8]) -> Option<Vec<u8>> {
183 #[cfg(test)]
184 {
185 return tests::storage_read(key);
186 }
187
188 #[cfg(not(test))]
189 match unsafe { sys::storage_read(key.as_ptr() as _, key.len() as _, ATOMIC_OP_REGISTER) } {
190 0 => None,
191 1 => Some(expect_register(read_register(ATOMIC_OP_REGISTER))),
192 _ => abort(),
193 }
194}
195
196pub fn storage_write_perm() -> bool {
198 match unsafe { sys::storage_write_perm() } {
199 0 => false,
200 1 => true,
201 _ => abort(),
202 }
203}
204
205pub fn contract_owner_address() -> Address {
207 #[cfg(test)]
208 {
209 return tests::contract_owner_address();
210 }
211 #[cfg(not(test))]
212 method_into_register!(contract_owner_address)
213 .try_into()
214 .unwrap_or_else(|_| abort())
215}
216
217pub fn caller_address() -> Address {
219 #[cfg(test)]
220 {
221 return tests::caller_address();
222 }
223 #[cfg(not(test))]
224 method_into_register!(caller_address)
225 .try_into()
226 .unwrap_or_else(|_| abort())
227}
228
229pub fn contract_instance_address() -> Address {
231 #[cfg(test)]
232 {
233 return tests::contract_instance_address();
234 }
235 #[cfg(not(test))]
236 method_into_register!(contract_instance_address)
237 .try_into()
238 .unwrap_or_else(|_| abort())
239}
240
241pub fn contract_owner_address_of(instance_address: Address) -> Address {
247 let addr = instance_address.as_bytes();
248 unsafe {
249 l1x_sys::contract_owner_address_of(addr.as_ptr() as _, addr.len() as _, ATOMIC_OP_REGISTER);
250 }
251 let maybe_addr = expect_register(read_register(ATOMIC_OP_REGISTER));
252 Address::try_from(maybe_addr).expect("VM returned an incorrect address")
253}
254
255pub fn contract_code_owner_address_of(code_address: Address) -> Address {
261 let addr = code_address.as_bytes();
262 unsafe {
263 l1x_sys::contract_code_owner_address_of(
264 addr.as_ptr() as _,
265 addr.len() as _,
266 ATOMIC_OP_REGISTER,
267 );
268 }
269 let maybe_addr = expect_register(read_register(ATOMIC_OP_REGISTER));
270 Address::try_from(maybe_addr).expect("VM returned an incorrect address")
271}
272
273pub fn contract_code_address_of(instance_address: Address) -> Address {
279 let addr = instance_address.as_bytes();
280 unsafe {
281 l1x_sys::contract_code_address_of(addr.as_ptr() as _, addr.len() as _, ATOMIC_OP_REGISTER);
282 }
283 let maybe_addr = expect_register(read_register(ATOMIC_OP_REGISTER));
284 Address::try_from(maybe_addr).expect("VM returned an incorrect address")
285}
286
287pub fn address_balance(address: &Address) -> Balance {
291 let address_vec = address.to_vec();
292 unsafe {
293 l1x_sys::address_balance(
294 address_vec.as_ptr() as _,
295 address_vec.len() as _,
296 ATOMIC_OP_REGISTER,
297 )
298 };
299 let bytes = expect_register(read_register(ATOMIC_OP_REGISTER));
300
301 u128::from_le_bytes(bytes.try_into().unwrap_or_else(|_| abort()))
302}
303
304pub fn transfer_to(to: &Address, amount: Balance) {
310 let to_address_vec = to.to_vec();
311 let amount = amount.to_le_bytes();
312 match unsafe {
313 l1x_sys::transfer_to(
314 to_address_vec.as_ptr() as _,
315 to_address_vec.len() as _,
316 amount.as_ptr() as _,
317 amount.len() as _,
318 )
319 } {
320 1 => (),
321 0 => crate::panic("Transfer tokens from the contract balance failed"),
322 _ => abort(),
323 };
324}
325
326pub fn transfer_from_caller(amount: Balance) {
332 let amount = amount.to_le_bytes();
333 match unsafe { l1x_sys::transfer_from_caller(amount.as_ptr() as _, amount.len() as _) } {
334 1 => (),
335 0 => crate::panic("Transfer tokens from the caller balance failed"),
336 _ => abort(),
337 }
338}
339
340pub fn block_hash() -> BlockHash {
342 let mut buf = BlockHash::default();
343
344 unsafe { l1x_sys::block_hash(buf.as_mut_ptr() as _, buf.len() as _) };
345
346 buf
347}
348
349pub fn block_number() -> BlockNumber {
351 let mut buf = [0u8; std::mem::size_of::<BlockNumber>()];
352
353 unsafe { l1x_sys::block_number(buf.as_mut_ptr() as _, buf.len() as _) };
354
355 BlockNumber::from_le_bytes(buf)
356}
357
358pub fn block_timestamp() -> TimeStamp {
360 let mut buf = [0u8; std::mem::size_of::<TimeStamp>()];
361
362 unsafe { l1x_sys::block_timestamp(buf.as_mut_ptr() as _, buf.len() as _) };
363
364 TimeStamp::from_le_bytes(buf)
365}
366
367pub fn gas_limit() -> Gas {
369 unsafe { l1x_sys::gas_limit() }
370}
371
372pub fn gas_left() -> Gas {
374 unsafe { l1x_sys::gas_left() }
375}
376
377pub fn deposit() -> Balance {
379 let mut buf = [0u8; std::mem::size_of::<TimeStamp>()];
380
381 unsafe { l1x_sys::deposit(buf.as_mut_ptr() as _, buf.len() as _) };
382
383 Balance::from_le_bytes(buf)
384}
385
386pub fn contract_instance_balance() -> Balance {
388 address_balance(&contract_instance_address())
389}
390
391pub fn call_contract(call: &ContractCall) -> Result<Vec<u8>, String> {
399 let call = call
400 .try_to_vec()
401 .expect("Can't serialize the function arguments");
402 match unsafe { sys::call_contract2(call.as_ptr() as _, call.len() as _, ATOMIC_OP_REGISTER) } {
403 0 => Err(
404 String::from_utf8_lossy(&expect_register(read_register(ATOMIC_OP_REGISTER)))
405 .to_string(),
406 ),
407 1 => Ok(expect_register(read_register(ATOMIC_OP_REGISTER))),
408 _ => abort(),
409 }
410}
411
412pub fn emit_event_experimental<T>(event: T)
414where
415 T: BorshSerialize,
416{
417 let event_data = event.try_to_vec().expect("Can't serialize the event");
418 match unsafe { sys::emit_event_experimental(event_data.as_ptr() as _, event_data.len() as _) } {
419 0 => abort(),
420 _ => (),
421 }
422}
423
424#[cfg(test)]
425mod tests {
426
427 use crate::types::Address;
428 use std::cell::RefCell;
429 use std::collections::HashMap;
430
431 thread_local! {
432 static MOCK_DATA: RefCell<MockData> = RefCell::new(MockData::new());
433 }
434
435 const CONTRACT_OWNER_ADDRESS: &[u8; 20] = b"mock_owner_address11";
436 const CONTRACT_INSTANCE_ADDRESS: &[u8; 20] = b"mock_instance_addres";
437 const CALLER_ADDRESS: &[u8; 20] = b"mock_caller_address1";
438
439 pub struct MockData {
440 storage: HashMap<Vec<u8>, Vec<u8>>,
441 input: Option<Vec<u8>>,
442 output: Vec<u8>,
443 messages: Vec<String>,
444 contract_owner_address: Address,
445 caller_address: Address,
446 contract_instance_address: Address,
447 }
448
449 impl MockData {
450 pub fn new() -> Self {
451 Self {
452 storage: HashMap::new(),
453 input: Some(Vec::new()),
454 output: Vec::new(),
455 messages: Vec::new(),
456 contract_owner_address: Address::test_create_address(
457 &CONTRACT_OWNER_ADDRESS.to_vec(),
458 ),
459 caller_address: Address::test_create_address(&CALLER_ADDRESS.to_vec()),
460 contract_instance_address: Address::test_create_address(
461 &CONTRACT_INSTANCE_ADDRESS.to_vec(),
462 ),
463 }
464 }
465 }
466
467 pub fn storage_write(key: &[u8], value: &[u8]) -> bool {
468 MOCK_DATA.with(|data| {
469 let mut mock_data = data.borrow_mut();
470 let is_new_insertion = !mock_data.storage.contains_key(key);
472 mock_data.storage.insert(key.to_vec(), value.to_vec());
473 is_new_insertion
474 })
475 }
476
477 pub fn storage_read(key: &[u8]) -> Option<Vec<u8>> {
478 MOCK_DATA.with(|data| data.borrow().storage.get(key).cloned())
479 }
480
481 pub fn storage_remove(key: &[u8]) -> bool {
482 MOCK_DATA.with(|data| data.borrow_mut().storage.remove(key).is_some())
483 }
484
485 pub fn contract_owner_address() -> Address {
486 MOCK_DATA.with(|data| data.borrow().contract_owner_address.clone())
487 }
488
489 pub fn caller_address() -> Address {
490 MOCK_DATA.with(|data| data.borrow().caller_address.clone())
491 }
492
493 pub fn contract_instance_address() -> Address {
494 MOCK_DATA.with(|data| data.borrow().contract_instance_address.clone())
495 }
496
497 pub fn remove_from_mock_storage(key: &[u8]) -> bool {
498 MOCK_DATA.with(|data| data.borrow_mut().storage.remove(key).is_some())
499 }
500
501 pub fn input() -> Option<Vec<u8>> {
502 MOCK_DATA.with(|data| data.borrow().input.clone())
503 }
504
505 pub fn output(data: &[u8]) {
506 MOCK_DATA.with(|data_refcell| {
507 let mut data_inside = data_refcell.borrow_mut();
508 data_inside.output = data.to_vec();
509 })
510 }
511
512 pub fn msg(message: &str) {
513 MOCK_DATA.with(|data| data.borrow_mut().messages.push(message.to_owned()))
514 }
515
516 pub fn set_mock_input(data: Vec<u8>) {
517 MOCK_DATA.with(|data_refcell| {
518 let mut data_inside = data_refcell.borrow_mut();
519 data_inside.input = Some(data);
520 });
521 }
522
523 pub fn get_mock_output() -> Vec<u8> {
524 MOCK_DATA.with(|data| data.borrow().output.clone())
525 }
526
527 pub fn get_mock_msgs() -> Vec<String> {
528 MOCK_DATA.with(|data| data.borrow().messages.clone())
529 }
530
531 pub fn clear_mock_io() {
532 MOCK_DATA.with(|data| {
533 let mut data = data.borrow_mut();
534 data.input = None;
535 data.output = Vec::new();
536 data.messages = Vec::new();
537 })
538 }
539
540 pub fn set_mock_contract_owner_address(owner_address: Vec<u8>) {
541 MOCK_DATA.with(|data| {
542 data.borrow_mut().contract_owner_address = Address::test_create_address(&owner_address)
543 })
544 }
545
546 pub fn set_mock_caller_address(caller_address: Vec<u8>) {
547 MOCK_DATA.with(|data| {
548 data.borrow_mut().caller_address = Address::test_create_address(&caller_address)
549 })
550 }
551
552 pub fn set_mock_contract_instance_address(contract_instance_address: Vec<u8>) {
553 MOCK_DATA.with(|data| {
554 data.borrow_mut().contract_instance_address =
555 Address::test_create_address(&contract_instance_address)
556 })
557 }
558
559 #[test]
561 fn test_storage() {
562 let key = b"key";
564 let value = b"value";
565
566 assert!(storage_write(key, value));
568
569 let stored_value = storage_read(key).unwrap();
571 assert_eq!(stored_value, value);
572
573 assert!(storage_remove(key));
575
576 assert!(storage_read(key).is_none());
578 }
579
580 #[test]
581 fn test_msg() {
582 let message = "Test message";
583 msg(message);
584
585 let mock_messages = get_mock_msgs();
586 assert_eq!(mock_messages.len(), 1);
587 assert_eq!(mock_messages[0], message);
588 }
589
590 #[test]
591 fn test_input_output() {
592 let data = vec![1, 2, 3, 4];
593
594 set_mock_input(data.clone());
595
596 let input_data = input().unwrap();
598 assert_eq!(input_data, data);
599
600 output(&data);
602
603 let output_data = get_mock_output();
605 assert_eq!(output_data, data);
606
607 clear_mock_io();
609
610 assert!(input().is_none());
612 assert!(get_mock_output().is_empty());
613 }
614
615 #[test]
616 fn test_storage_write_and_read() {
617 let key = vec![1, 2, 3];
618 let value = vec![4, 5, 6];
619
620 storage_write(&key, &value);
622
623 let stored_value = storage_read(&key).unwrap();
625 assert_eq!(stored_value, value);
626 }
627
628 #[test]
629 fn test_remove_from_mock_storage() {
630 let key = vec![1, 2, 3];
631 let value = vec![4, 5, 6];
632
633 storage_write(&key, &value);
635 remove_from_mock_storage(&key);
636
637 let stored_value = storage_read(&key);
639 assert!(stored_value.is_none());
640 }
641
642 #[test]
643 fn test_contract_owner_address_and_caller_address() {
644 let mock_owner_address = b"current_address12345".to_vec();
645 let mock_caller_address = b"caller_address123456".to_vec();
646 let mock_instance_address = b"instance_address3456".to_vec();
647
648 set_mock_contract_owner_address(mock_owner_address.clone());
650 set_mock_caller_address(mock_caller_address.clone());
651 set_mock_contract_instance_address(mock_instance_address.clone());
652
653 assert_eq!(
655 contract_owner_address(),
656 Address::test_create_address(&mock_owner_address)
657 );
658
659 assert_eq!(
661 caller_address(),
662 Address::test_create_address(&mock_caller_address)
663 );
664
665 assert_eq!(
666 contract_instance_address(),
667 Address::test_create_address(&mock_instance_address)
668 );
669 }
670
671 #[test]
672 fn test_input_and_output() {
673 let data = vec![1, 2, 3];
674
675 set_mock_input(data.clone());
677 assert_eq!(input().unwrap(), data);
678
679 output(&data);
681
682 assert_eq!(get_mock_output(), data);
684 }
685
686 #[test]
687 fn test_clear_mock_io() {
688 set_mock_input(vec![1, 2, 3]);
690 output(&vec![4, 5, 6]);
691 msg("Hello, world!");
692
693 clear_mock_io();
695
696 assert!(input().is_none());
698 assert_eq!(get_mock_output(), vec![] as Vec<u8>);
699 assert_eq!(get_mock_msgs(), Vec::<String>::new());
700 }
701}