casper_contract_sdk/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(not(feature = "std"))]
4#[macro_use]
5extern crate alloc;
6
7pub mod abi;
8pub mod prelude;
9pub mod serializers;
10#[cfg(not(target_arch = "wasm32"))]
11pub use linkme;
12
13#[cfg(not(target_arch = "wasm32"))]
14pub mod abi_generator;
15pub mod casper;
16pub mod collections;
17pub mod contrib;
18#[cfg(feature = "std")]
19pub mod schema;
20pub mod types;
21
22use crate::prelude::{marker::PhantomData, ptr::NonNull};
23
24use crate::serializers::borsh::{BorshDeserialize, BorshSerialize};
25use casper::{CallResult, Entity};
26pub use casper_contract_macros as macros;
27pub use casper_contract_sdk_sys as sys;
28pub use casper_executor_wasm_common;
29use types::{Address, CallError};
30
31cfg_if::cfg_if! {
32    if #[cfg(feature = "std")] {
33        #[inline]
34        pub fn set_panic_hook() {
35            static SET_HOOK: std::sync::Once = std::sync::Once::new();
36            SET_HOOK.call_once(|| {
37                std::panic::set_hook(Box::new(|panic_info| {
38                    let msg = panic_info.to_string();
39                    casper::print(&msg);
40                }));
41            });
42        }
43    }
44    else {
45        pub fn set_panic_hook() {
46            // TODO: What to do?
47        }
48    }
49}
50
51pub fn reserve_vec_space(vec: &mut Vec<u8>, size: usize) -> Option<NonNull<u8>> {
52    if size == 0 {
53        None
54    } else {
55        *vec = Vec::with_capacity(size);
56        unsafe {
57            vec.set_len(size);
58        }
59        NonNull::new(vec.as_mut_ptr())
60    }
61}
62
63pub trait ContractRef {
64    fn new() -> Self;
65}
66
67pub trait ToCallData {
68    type Return<'a>;
69
70    fn entry_point(&self) -> &str;
71
72    fn input_data(&self) -> Option<crate::prelude::Vec<u8>>;
73}
74
75/// To derive this contract you have to use `#[casper]` macro on top of impl block.
76///
77/// This proc macro handles generation of a manifest.
78pub trait Contract {
79    type Ref: ContractRef;
80
81    fn name() -> &'static str;
82    fn create<T: ToCallData>(
83        value: u64,
84        call_data: T,
85    ) -> Result<ContractHandle<Self::Ref>, CallError>;
86    fn default_create() -> Result<ContractHandle<Self::Ref>, CallError>;
87    fn upgrade<T: ToCallData>(code: Option<&[u8]>, call_data: T) -> Result<(), CallError>;
88}
89
90#[derive(Debug)]
91pub enum Access {
92    Private,
93    Public,
94}
95
96// A println! like macro that calls `host::print` function.
97#[cfg(target_arch = "wasm32")]
98#[macro_export]
99macro_rules! log {
100    ($($arg:tt)*) => ({
101        $crate::prelude::casper::print(&$crate::prelude::format!($($arg)*));
102    })
103}
104
105#[cfg(not(target_arch = "wasm32"))]
106#[macro_export]
107macro_rules! log {
108    ($($arg:tt)*) => ({
109        eprintln!("📝 {}", &$crate::prelude::format!($($arg)*));
110    })
111}
112
113#[macro_export]
114macro_rules! revert {
115    () => {{
116        $crate::casper::ret(
117            $crate::casper_executor_wasm_common::flags::ReturnFlags::REVERT,
118            None,
119        );
120        unreachable!()
121    }};
122    ($arg:expr) => {{
123        let value = $arg;
124        let data =
125            $crate::serializers::borsh::to_vec(&value).expect("Revert value should serialize");
126        $crate::casper::ret(
127            $crate::casper_executor_wasm_common::flags::ReturnFlags::REVERT,
128            Some(data.as_slice()),
129        );
130        #[allow(unreachable_code)]
131        value
132    }};
133}
134
135pub trait UnwrapOrRevert<T> {
136    /// Unwraps the value into its inner type or calls [`crate::casper::ret`] with a
137    /// predetermined error code on failure.
138    fn unwrap_or_revert(self) -> T;
139}
140
141impl<T, E> UnwrapOrRevert<T> for Result<T, E>
142where
143    E: BorshSerialize,
144{
145    fn unwrap_or_revert(self) -> T {
146        self.unwrap_or_else(|error| {
147            let error_data = borsh::to_vec(&error).expect("Revert value should serialize");
148            casper::ret(
149                casper_executor_wasm_common::flags::ReturnFlags::REVERT,
150                Some(error_data.as_slice()),
151            );
152            unreachable!("Support for unwrap_or_revert")
153        })
154    }
155}
156
157#[derive(Debug)]
158pub struct ContractHandle<T: ContractRef> {
159    contract_address: Address,
160    marker: PhantomData<T>,
161}
162
163impl<T: ContractRef> ContractHandle<T> {
164    #[must_use]
165    pub const fn from_address(contract_address: Address) -> Self {
166        ContractHandle {
167            contract_address,
168            marker: PhantomData,
169        }
170    }
171
172    pub fn build_call(&self) -> CallBuilder<T> {
173        CallBuilder {
174            address: self.contract_address,
175            marker: PhantomData,
176            transferred_value: None,
177        }
178    }
179
180    /// A shorthand form to call contracts with default settings.
181    #[inline]
182    pub fn call<'a, CallData: ToCallData>(
183        &self,
184        func: impl FnOnce(T) -> CallData,
185    ) -> Result<CallData::Return<'a>, CallError>
186    where
187        CallData::Return<'a>: BorshDeserialize,
188    {
189        self.build_call().call(func)
190    }
191
192    /// A shorthand form to call contracts with default settings.
193    #[inline]
194    pub fn try_call<CallData: ToCallData>(
195        &self,
196        func: impl FnOnce(T) -> CallData,
197    ) -> Result<CallResult<CallData>, CallError> {
198        self.build_call().try_call(func)
199    }
200
201    #[must_use]
202    pub fn contract_address(&self) -> Address {
203        self.contract_address
204    }
205
206    #[must_use]
207    pub fn entity(&self) -> Entity {
208        Entity::Contract(self.contract_address)
209    }
210
211    /// Returns the balance of the contract.
212    #[must_use]
213    pub fn balance(&self) -> u64 {
214        casper::get_balance_of(&Entity::Contract(self.contract_address))
215    }
216}
217
218pub struct CallBuilder<T: ContractRef> {
219    address: Address,
220    transferred_value: Option<u64>,
221    marker: PhantomData<T>,
222}
223
224impl<T: ContractRef> CallBuilder<T> {
225    #[must_use]
226    pub fn new(address: Address) -> Self {
227        CallBuilder {
228            address,
229            transferred_value: None,
230            marker: PhantomData,
231        }
232    }
233
234    #[must_use]
235    pub fn with_transferred_value(mut self, transferred_value: u64) -> Self {
236        self.transferred_value = Some(transferred_value);
237        self
238    }
239
240    /// Casts the call builder to a different contract reference.
241    #[must_use]
242    pub fn cast<U: ContractRef>(self) -> CallBuilder<U> {
243        CallBuilder {
244            address: self.address,
245            transferred_value: self.transferred_value,
246            marker: PhantomData,
247        }
248    }
249
250    pub fn try_call<CallData: ToCallData>(
251        &self,
252        func: impl FnOnce(T) -> CallData,
253    ) -> Result<CallResult<CallData>, CallError> {
254        let inst = T::new();
255        let call_data = func(inst);
256        casper::call(
257            &self.address,
258            self.transferred_value.unwrap_or(0),
259            call_data,
260        )
261    }
262
263    pub fn call<'a, CallData: ToCallData>(
264        &self,
265        func: impl FnOnce(T) -> CallData,
266    ) -> Result<CallData::Return<'a>, CallError>
267    where
268        CallData::Return<'a>: BorshDeserialize,
269    {
270        let inst = T::new();
271        let call_data = func(inst);
272        let call_result = casper::call(
273            &self.address,
274            self.transferred_value.unwrap_or(0),
275            call_data,
276        )?;
277        call_result.into_result()
278    }
279}
280
281pub struct ContractBuilder<'a, T: ContractRef> {
282    transferred_value: Option<u64>,
283    code: Option<&'a [u8]>,
284    seed: Option<&'a [u8; 32]>,
285    marker: PhantomData<T>,
286}
287
288impl<T: ContractRef> Default for ContractBuilder<'_, T> {
289    fn default() -> Self {
290        Self::new()
291    }
292}
293
294impl<'a, T: ContractRef> ContractBuilder<'a, T> {
295    #[must_use]
296    pub fn new() -> Self {
297        ContractBuilder {
298            transferred_value: None,
299            code: None,
300            seed: None,
301            marker: PhantomData,
302        }
303    }
304
305    #[must_use]
306    pub fn with_transferred_value(mut self, transferred_value: u64) -> Self {
307        self.transferred_value = Some(transferred_value);
308        self
309    }
310
311    #[must_use]
312    pub fn with_code(mut self, code: &'a [u8]) -> Self {
313        self.code = Some(code);
314        self
315    }
316
317    #[must_use]
318    pub fn with_seed(mut self, seed: &'a [u8; 32]) -> Self {
319        self.seed = Some(seed);
320        self
321    }
322
323    pub fn create<CallData: ToCallData>(
324        &self,
325        func: impl FnOnce() -> CallData,
326    ) -> Result<ContractHandle<T>, CallError>
327    where
328        CallData::Return<'a>: BorshDeserialize,
329    {
330        let value = self.transferred_value.unwrap_or(0);
331        let call_data = func();
332        let input_data = call_data.input_data();
333        let seed = self.seed;
334        let create_result = casper::create(
335            self.code,
336            value,
337            Some(call_data.entry_point()),
338            input_data.as_deref(),
339            seed,
340        )?;
341        Ok(ContractHandle::from_address(create_result.contract_address))
342    }
343
344    pub fn default_create(&self) -> Result<ContractHandle<T>, CallError> {
345        if self.transferred_value.is_some() {
346            panic!("Value should not be set for default create");
347        }
348
349        let value = self.transferred_value.unwrap_or(0);
350        let seed = self.seed;
351        let create_result = casper::create(self.code, value, None, None, seed)?;
352        Ok(ContractHandle::from_address(create_result.contract_address))
353    }
354}
355
356/// Trait for converting a message data to a string.
357pub trait Message: BorshSerialize {
358    const TOPIC: &'static str;
359    /// Converts the message data to a string.
360    fn payload(&self) -> Vec<u8>;
361}
362
363#[cfg(test)]
364mod tests {
365    #[test]
366    fn test_call_builder() {}
367}