1#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
2pub mod native;
3
4use crate::{
5 abi::{CasperABI, EnumVariant},
6 prelude::{
7 ffi::c_void,
8 marker::PhantomData,
9 mem::MaybeUninit,
10 ptr::{self, NonNull},
11 },
12 reserve_vec_space,
13 serializers::borsh::{BorshDeserialize, BorshSerialize},
14 types::{Address, CallError},
15 Message, ToCallData,
16};
17
18use casper_contract_sdk_sys::casper_env_info;
19use casper_executor_wasm_common::{
20 env_info::EnvInfo,
21 error::{result_from_code, CommonResult, HOST_ERROR_SUCCESS},
22 flags::ReturnFlags,
23 keyspace::{Keyspace, KeyspaceTag},
24};
25
26#[inline]
28pub fn print(msg: &str) {
29 unsafe { casper_contract_sdk_sys::casper_print(msg.as_ptr(), msg.len()) };
30}
31
32pub enum Alloc<F: FnOnce(usize) -> Option<ptr::NonNull<u8>>> {
33 Callback(F),
34 Static(ptr::NonNull<u8>),
35}
36
37extern "C" fn alloc_callback<F: FnOnce(usize) -> Option<ptr::NonNull<u8>>>(
38 len: usize,
39 ctx: *mut c_void,
40) -> *mut u8 {
41 let opt_closure = ctx.cast::<Option<F>>();
42 let allocated_ptr = unsafe { (*opt_closure).take().unwrap()(len) };
43 match allocated_ptr {
44 Some(ptr) => ptr.as_ptr(),
45 None => ptr::null_mut(),
46 }
47}
48
49pub fn copy_input_into<F: FnOnce(usize) -> Option<ptr::NonNull<u8>>>(
52 alloc: Option<F>,
53) -> Option<NonNull<u8>> {
54 let ret = unsafe {
55 casper_contract_sdk_sys::casper_copy_input(
56 alloc_callback::<F>,
57 &alloc as *const _ as *mut c_void,
58 )
59 };
60 NonNull::<u8>::new(ret)
61}
62
63pub fn copy_input() -> Vec<u8> {
65 let mut vec = Vec::new();
66 let last_ptr = copy_input_into(Some(|size| reserve_vec_space(&mut vec, size)));
67 match last_ptr {
68 Some(_last_ptr) => vec,
69 None => {
70 Vec::new()
73 }
74 }
75}
76
77pub fn copy_input_to(dest: &mut [u8]) -> Option<&[u8]> {
79 let last_ptr = copy_input_into(Some(|size| {
80 if size > dest.len() {
81 None
82 } else {
83 Some(unsafe { ptr::NonNull::new_unchecked(dest.as_mut_ptr()) })
86 }
87 }));
88
89 let end_ptr = last_ptr?;
90 let length = unsafe { end_ptr.as_ptr().offset_from(dest.as_mut_ptr()) };
91 let length: usize = length.try_into().unwrap();
92 Some(&dest[..length])
93}
94
95pub fn ret(flags: ReturnFlags, data: Option<&[u8]>) {
97 let (data_ptr, data_len) = match data {
98 Some(data) => (data.as_ptr(), data.len()),
99 None => (ptr::null(), 0),
100 };
101 unsafe { casper_contract_sdk_sys::casper_return(flags.bits(), data_ptr, data_len) };
102 #[cfg(target_arch = "wasm32")]
103 unreachable!()
104}
105
106pub fn read<F: FnOnce(usize) -> Option<ptr::NonNull<u8>>>(
108 key: Keyspace,
109 f: F,
110) -> Result<Option<()>, CommonResult> {
111 let (key_space, key_bytes) = match key {
112 Keyspace::State => (KeyspaceTag::State as u64, &[][..]),
113 Keyspace::Context(key_bytes) => (KeyspaceTag::Context as u64, key_bytes),
114 Keyspace::NamedKey(key_bytes) => (KeyspaceTag::NamedKey as u64, key_bytes.as_bytes()),
115 Keyspace::PaymentInfo(payload) => (KeyspaceTag::PaymentInfo as u64, payload.as_bytes()),
116 };
117
118 let mut info = casper_contract_sdk_sys::ReadInfo {
119 data: ptr::null(),
120 size: 0,
121 };
122
123 extern "C" fn alloc_cb<F: FnOnce(usize) -> Option<ptr::NonNull<u8>>>(
124 len: usize,
125 ctx: *mut c_void,
126 ) -> *mut u8 {
127 let opt_closure = ctx as *mut Option<F>;
128 let allocated_ptr = unsafe { (*opt_closure).take().unwrap()(len) };
129 match allocated_ptr {
130 Some(mut ptr) => unsafe { ptr.as_mut() },
131 None => ptr::null_mut(),
132 }
133 }
134
135 let ctx = &Some(f) as *const _ as *mut _;
136
137 let ret = unsafe {
138 casper_contract_sdk_sys::casper_read(
139 key_space,
140 key_bytes.as_ptr(),
141 key_bytes.len(),
142 &mut info as *mut casper_contract_sdk_sys::ReadInfo,
143 alloc_cb::<F>,
144 ctx,
145 )
146 };
147
148 match result_from_code(ret) {
149 Ok(()) => Ok(Some(())),
150 Err(CommonResult::NotFound) => Ok(None),
151 Err(err) => Err(err),
152 }
153}
154
155pub fn write(key: Keyspace, value: &[u8]) -> Result<(), CommonResult> {
157 let (key_space, key_bytes) = match key {
158 Keyspace::State => (KeyspaceTag::State as u64, &[][..]),
159 Keyspace::Context(key_bytes) => (KeyspaceTag::Context as u64, key_bytes),
160 Keyspace::NamedKey(key_bytes) => (KeyspaceTag::NamedKey as u64, key_bytes.as_bytes()),
161 Keyspace::PaymentInfo(payload) => (KeyspaceTag::PaymentInfo as u64, payload.as_bytes()),
162 };
163 let ret = unsafe {
164 casper_contract_sdk_sys::casper_write(
165 key_space,
166 key_bytes.as_ptr(),
167 key_bytes.len(),
168 value.as_ptr(),
169 value.len(),
170 )
171 };
172 result_from_code(ret)
173}
174
175pub fn remove(key: Keyspace) -> Result<(), CommonResult> {
177 let (key_space, key_bytes) = match key {
178 Keyspace::State => (KeyspaceTag::State as u64, &[][..]),
179 Keyspace::Context(key_bytes) => (KeyspaceTag::Context as u64, key_bytes),
180 Keyspace::NamedKey(key_bytes) => (KeyspaceTag::NamedKey as u64, key_bytes.as_bytes()),
181 Keyspace::PaymentInfo(payload) => (KeyspaceTag::PaymentInfo as u64, payload.as_bytes()),
182 };
183 let ret = unsafe {
184 casper_contract_sdk_sys::casper_remove(key_space, key_bytes.as_ptr(), key_bytes.len())
185 };
186 result_from_code(ret)
187}
188
189pub fn create(
191 code: Option<&[u8]>,
192 transferred_value: u64,
193 constructor: Option<&str>,
194 input_data: Option<&[u8]>,
195 seed: Option<&[u8; 32]>,
196) -> Result<casper_contract_sdk_sys::CreateResult, CallError> {
197 let (code_ptr, code_size): (*const u8, usize) = match code {
198 Some(code) => (code.as_ptr(), code.len()),
199 None => (ptr::null(), 0),
200 };
201
202 let mut result = MaybeUninit::uninit();
203
204 let call_error = unsafe {
205 casper_contract_sdk_sys::casper_create(
206 code_ptr,
207 code_size,
208 transferred_value,
209 constructor.map(|s| s.as_ptr()).unwrap_or(ptr::null()),
210 constructor.map(|s| s.len()).unwrap_or(0),
211 input_data.map(|s| s.as_ptr()).unwrap_or(ptr::null()),
212 input_data.map(|s| s.len()).unwrap_or(0),
213 seed.map(|s| s.as_ptr()).unwrap_or(ptr::null()),
214 seed.map(|s| s.len()).unwrap_or(0),
215 result.as_mut_ptr(),
216 )
217 };
218
219 if call_error == 0 {
220 let result = unsafe { result.assume_init() };
221 Ok(result)
222 } else {
223 Err(CallError::try_from(call_error).expect("Unexpected error code"))
224 }
225}
226
227pub(crate) fn call_into<F: FnOnce(usize) -> Option<ptr::NonNull<u8>>>(
228 address: &Address,
229 transferred_value: u64,
230 entry_point: &str,
231 input_data: &[u8],
232 alloc: Option<F>,
233) -> Result<(), CallError> {
234 let result_code = unsafe {
235 casper_contract_sdk_sys::casper_call(
236 address.as_ptr(),
237 address.len(),
238 transferred_value,
239 entry_point.as_ptr(),
240 entry_point.len(),
241 input_data.as_ptr(),
242 input_data.len(),
243 alloc_callback::<F>,
244 &alloc as *const _ as *mut _,
245 )
246 };
247 call_result_from_code(result_code)
248}
249
250fn call_result_from_code(result_code: u32) -> Result<(), CallError> {
251 if result_code == HOST_ERROR_SUCCESS {
252 Ok(())
253 } else {
254 Err(CallError::try_from(result_code).expect("Unexpected error code"))
255 }
256}
257
258pub fn casper_call(
260 address: &Address,
261 transferred_value: u64,
262 entry_point: &str,
263 input_data: &[u8],
264) -> (Option<Vec<u8>>, Result<(), CallError>) {
265 let mut output = None;
266 let result_code = call_into(
267 address,
268 transferred_value,
269 entry_point,
270 input_data,
271 Some(|size| {
272 let mut vec = Vec::new();
273 reserve_vec_space(&mut vec, size);
274 let result = Some(unsafe { ptr::NonNull::new_unchecked(vec.as_mut_ptr()) });
275 output = Some(vec);
276 result
277 }),
278 );
279 (output, result_code)
280}
281
282pub fn upgrade(
284 code: &[u8],
285 entry_point: Option<&str>,
286 input_data: Option<&[u8]>,
287) -> Result<(), CallError> {
288 let code_ptr = code.as_ptr();
289 let code_size = code.len();
290 let entry_point_ptr = entry_point.map(str::as_ptr).unwrap_or(ptr::null());
291 let entry_point_size = entry_point.map(str::len).unwrap_or(0);
292 let input_ptr = input_data.map(|s| s.as_ptr()).unwrap_or(ptr::null());
293 let input_size = input_data.map(|s| s.len()).unwrap_or(0);
294
295 let result_code = unsafe {
296 casper_contract_sdk_sys::casper_upgrade(
297 code_ptr,
298 code_size,
299 entry_point_ptr,
300 entry_point_size,
301 input_ptr,
302 input_size,
303 )
304 };
305 match call_result_from_code(result_code) {
306 Ok(()) => Ok(()),
307 Err(err) => Err(err),
308 }
309}
310
311pub fn read_into_vec(key: Keyspace) -> Result<Option<Vec<u8>>, CommonResult> {
313 let mut vec = Vec::new();
314 let out = read(key, |size| reserve_vec_space(&mut vec, size))?.map(|()| vec);
315 Ok(out)
316}
317
318pub fn has_state() -> Result<bool, CommonResult> {
320 let mut vec = Vec::new();
322 let read_info = read(Keyspace::State, |size| reserve_vec_space(&mut vec, size))?;
323 match read_info {
324 Some(()) => Ok(true),
325 None => Ok(false),
326 }
327}
328
329pub fn read_state<T: Default + BorshDeserialize>() -> Result<T, CommonResult> {
331 let mut vec = Vec::new();
332 let read_info = read(Keyspace::State, |size| reserve_vec_space(&mut vec, size))?;
333 match read_info {
334 Some(()) => Ok(borsh::from_slice(&vec).unwrap()),
335 None => Ok(T::default()),
336 }
337}
338
339pub fn write_state<T: BorshSerialize>(state: &T) -> Result<(), CommonResult> {
341 let new_state = borsh::to_vec(state).unwrap();
342 write(Keyspace::State, &new_state)?;
343 Ok(())
344}
345
346#[derive(Debug)]
347pub struct CallResult<T: ToCallData> {
348 pub data: Option<Vec<u8>>,
349 pub result: Result<(), CallError>,
350 pub marker: PhantomData<T>,
351}
352
353impl<T: ToCallData> CallResult<T> {
354 pub fn into_result<'a>(self) -> Result<T::Return<'a>, CallError>
355 where
356 <T as ToCallData>::Return<'a>: BorshDeserialize,
357 {
358 match self.result {
359 Ok(()) | Err(CallError::CalleeReverted) => {
360 let data = self.data.unwrap_or_default();
361 Ok(borsh::from_slice(&data).unwrap())
362 }
363 Err(call_error) => Err(call_error),
364 }
365 }
366
367 pub fn did_revert(&self) -> bool {
368 self.result == Err(CallError::CalleeReverted)
369 }
370}
371
372pub fn call<T: ToCallData>(
374 contract_address: &Address,
375 transferred_value: u64,
376 call_data: T,
377) -> Result<CallResult<T>, CallError> {
378 let input_data = call_data.input_data().unwrap_or_default();
379
380 let (maybe_data, result_code) = casper_call(
381 contract_address,
382 transferred_value,
383 call_data.entry_point(),
384 &input_data,
385 );
386 match result_code {
387 Ok(()) | Err(CallError::CalleeReverted) => Ok(CallResult::<T> {
388 data: maybe_data,
389 result: result_code,
390 marker: PhantomData,
391 }),
392 Err(error) => Err(error),
393 }
394}
395
396pub fn get_env_info() -> EnvInfo {
398 let ret = {
399 let mut info = MaybeUninit::<EnvInfo>::uninit();
400
401 let ret = unsafe { casper_env_info(info.as_mut_ptr().cast(), size_of::<EnvInfo>() as u32) };
402 result_from_code(ret).map(|()| {
403 unsafe { info.assume_init() }
405 })
406 };
407
408 match ret {
409 Ok(info) => info,
410 Err(err) => panic!("Failed to get environment info: {:?}", err),
411 }
412}
413
414#[must_use]
416pub fn get_caller() -> Entity {
417 let info = get_env_info();
418 Entity::from_parts(info.caller_kind, info.caller_addr).expect("Invalid caller kind")
419}
420
421#[must_use]
422pub fn get_callee() -> Entity {
423 let info = get_env_info();
424 Entity::from_parts(info.callee_kind, info.callee_addr).expect("Invalid callee kind")
425}
426
427#[derive(
429 BorshSerialize, BorshDeserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord,
430)]
431pub enum Entity {
432 Account([u8; 32]),
433 Contract([u8; 32]),
434}
435
436impl Entity {
437 #[must_use]
439 pub fn tag(&self) -> u32 {
440 match self {
441 Entity::Account(_) => 0,
442 Entity::Contract(_) => 1,
443 }
444 }
445
446 #[must_use]
447 pub fn from_parts(tag: u32, address: [u8; 32]) -> Option<Self> {
448 match tag {
449 0 => Some(Self::Account(address)),
450 1 => Some(Self::Contract(address)),
451 _ => None,
452 }
453 }
454
455 #[must_use]
456 pub fn address(&self) -> &Address {
457 match self {
458 Entity::Account(addr) | Entity::Contract(addr) => addr,
459 }
460 }
461}
462
463impl CasperABI for Entity {
464 fn populate_definitions(definitions: &mut crate::abi::Definitions) {
465 definitions.populate_one::<[u8; 32]>();
466 }
467
468 fn declaration() -> crate::abi::Declaration {
469 "Entity".into()
470 }
471
472 fn definition() -> crate::abi::Definition {
473 crate::abi::Definition::Enum {
474 items: vec![
475 EnumVariant {
476 name: "Account".into(),
477 discriminant: 0,
478 decl: <[u8; 32] as CasperABI>::declaration(),
479 },
480 EnumVariant {
481 name: "Contract".into(),
482 discriminant: 1,
483 decl: <[u8; 32] as CasperABI>::declaration(),
484 },
485 ],
486 }
487 }
488}
489
490#[must_use]
492pub fn get_balance_of(entity_kind: &Entity) -> u64 {
493 let (kind, addr) = match entity_kind {
494 Entity::Account(addr) => (0, addr),
495 Entity::Contract(addr) => (1, addr),
496 };
497 let mut output: MaybeUninit<u64> = MaybeUninit::uninit();
498 let ret = unsafe {
499 casper_contract_sdk_sys::casper_env_balance(
500 kind,
501 addr.as_ptr(),
502 addr.len(),
503 output.as_mut_ptr().cast(),
504 )
505 };
506 if ret == 1 {
507 unsafe { output.assume_init() }
508 } else {
509 0
510 }
511}
512
513#[must_use]
515pub fn transferred_value() -> u64 {
516 let info = get_env_info();
517 info.transferred_value
518}
519
520pub fn transfer(target_account: &Address, amount: u64) -> Result<(), CallError> {
522 let amount: *const c_void = &amount as *const _ as *const c_void;
523 let result_code = unsafe {
524 casper_contract_sdk_sys::casper_transfer(
525 target_account.as_ptr(),
526 target_account.len(),
527 amount,
528 )
529 };
530 call_result_from_code(result_code)
531}
532
533#[inline]
535pub fn get_block_time() -> u64 {
536 let info = get_env_info();
537 info.block_time
538}
539
540#[doc(hidden)]
541pub fn emit_raw(topic: &str, payload: &[u8]) -> Result<(), CommonResult> {
542 let ret = unsafe {
543 casper_contract_sdk_sys::casper_emit(
544 topic.as_ptr(),
545 topic.len(),
546 payload.as_ptr(),
547 payload.len(),
548 )
549 };
550 result_from_code(ret)
551}
552
553pub fn emit<M>(message: M) -> Result<(), CommonResult>
555where
556 M: Message,
557{
558 let topic = M::TOPIC;
559 let payload = message.payload();
560 emit_raw(topic, &payload)
561}