polymesh_ink/
macros.rs

1#[macro_export]
2macro_rules! upgradable_api {
3    (
4        $(#[$mod_attr:meta])*
5        mod $mod_name:ident {
6            impl $api_type:ident {
7                $(
8                    $(#[doc = $doc_attr:tt])*
9                    $(#[ink($ink_attr:tt)])*
10                    $fn_vis:vis fn $fn_name:ident(
11                        & $self:ident
12                        $(,)?
13                        $($param:ident: $ty:ty),*
14                        $(,)?
15                    ) -> $fn_return:ty {
16                        $( $fn_impl:tt )*
17                    }
18                )*
19            }
20            $(
21            impl $api_type2:ident {
22                $(
23                    $(#[doc = $doc2_attr:tt])*
24                    $fn2_vis:vis fn $fn2_name:ident(
25                        $(& $self2:ident)?
26                        $(,)?
27                        $($param2:ident: $ty2:ty),*
28                        $(,)?
29                    ) -> $fn2_return:ty {
30                        $( $fn2_impl:tt )*
31                    }
32                )*
33            }
34            )?
35        }
36    ) => {
37        pub use $mod_name::*;
38
39        #[cfg_attr(not(feature = "as-library"), ink::contract(env = PolymeshEnvironment))]
40        #[cfg(not(feature = "as-library"))]
41        mod $mod_name {
42            use super::*;
43
44            #[ink(storage)]
45            pub struct $api_type {
46            }
47
48            impl $api_type {
49                 #[ink(constructor)]
50                 pub fn new() -> Self {
51                     panic!("Only upload this contract, don't deploy it.");
52                 }
53             }
54
55            impl $api_type {
56                $(
57                    $(#[doc = $doc_attr])*
58                    $(#[ink($ink_attr, payable)])*
59                    $fn_vis fn $fn_name(&self, $($param: $ty),*) -> $fn_return {
60                        ::paste::paste! {
61                            self.[<__impl_ $fn_name>]($($param),*)
62                        }
63                    }
64                )*
65            }
66
67            impl $api_type {
68                $(
69                    ::paste::paste! {
70                        fn [<__impl_ $fn_name>](&$self, $($param: $ty),*) -> $fn_return {
71                            $( $fn_impl )*
72                        }
73                    }
74                )*
75            }
76            $(
77            impl $api_type {
78                $(
79                    $(#[doc = $doc2_attr])*
80                    $fn2_vis fn $fn2_name($(&$self2,)? $($param2: $ty2),*) -> $fn2_return {
81                        $( $fn2_impl )*
82                    }
83                )*
84            }
85            )?
86        }
87
88        #[cfg(feature = "as-library")]
89        mod $mod_name {
90            use super::*;
91
92            /// Upgradable wrapper for the Polymesh Runtime API.
93            ///
94            /// Contracts can use this to maintain support accross
95            /// major Polymesh releases.
96            #[derive(Clone, Debug, Default, scale::Encode, scale::Decode)]
97            #[cfg_attr(feature = "std", derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout))]
98            pub struct $api_type {
99                hash: Hash,
100            }
101
102            impl $api_type {
103                pub fn new() -> PolymeshResult<Self> {
104                    Ok(Self {
105                      hash: Self::get_latest_upgrade()?,
106                    })
107                }
108
109                pub fn new_with_hash(hash: Hash) -> Self {
110                    Self { hash }
111                }
112
113                /// Update code hash.
114                pub fn update_code_hash(&mut self, hash: Hash) {
115                    self.hash = hash;
116                }
117
118                pub fn check_for_upgrade(&mut self) -> PolymeshResult<()> {
119                    self.hash = Self::get_latest_upgrade()?;
120                    Ok(())
121                }
122
123                fn get_latest_upgrade() -> PolymeshResult<Hash> {
124                    let extension = <<PolymeshEnvironment as ink::env::Environment>::ChainExtension as ink::ChainExtensionInstance>::instantiate();
125                    Ok(extension.get_latest_api_upgrade((&API_VERSION).into())?.into())
126                }
127
128                $(
129                    $crate::upgradable_api! {
130                        @impl_api_func
131                        $(#[doc = $doc_attr])*
132                        $(#[ink($ink_attr)])*
133                        $fn_vis fn $fn_name(
134                            &$self,
135                            $($param: $ty),*
136                        ) -> $fn_return {
137                            $( $fn_impl )*
138                        }
139                    }
140                )*
141            }
142            $(
143            impl $api_type {
144                $(
145                    $(#[doc = $doc2_attr])*
146                    $fn2_vis fn $fn2_name($(&$self2,)? $($param2: $ty2),*) -> $fn2_return {
147                        $( $fn2_impl )*
148                    }
149                )*
150            }
151            )?
152        }
153    };
154    // Upgradable api method.
155    (@impl_api_func
156        $(#[doc = $doc_attr:tt])*
157        $(#[ink($ink_attr:tt)])+
158        $fn_vis:vis fn $fn_name:ident(
159            & $self:ident
160            $(,)?
161            $($param:ident: $ty:ty),*
162            $(,)?
163        ) -> $fn_return:ty {
164            $( $fn_impl:tt )*
165        }
166    ) => {
167        $(#[doc = $doc_attr])*
168        $fn_vis fn $fn_name(&$self, $($param: $ty),*) -> $fn_return {
169            use ink::env::call::{ExecutionInput, Selector};
170            const FUNC: &'static str = stringify!{$fn_name};
171            let selector = Selector::new(::polymesh_api::ink::blake2_256(FUNC.as_bytes())[..4]
172              .try_into().unwrap());
173            ink::env::call::build_call::<ink::env::DefaultEnvironment>()
174                .delegate($self.hash)
175                .exec_input(
176                    ExecutionInput::new(selector)
177                        .push_arg(($($param),*)),
178                )
179                .returns::<$fn_return>()
180                .invoke()
181        }
182    };
183    // Non-upgradable api method.
184    (@impl_api_func
185        $(#[doc = $doc_attr:tt])*
186        $fn_vis:vis fn $fn_name:ident(
187            $(& $self:ident)?
188            $(,)?
189            $($param:ident: $ty:ty),*
190            $(,)?
191        ) -> $fn_return:ty {
192            $( $fn_impl:tt )*
193        }
194    ) => {
195        $(#[doc = $doc_attr])*
196        $fn_vis fn $fn_name($(&$self,)? $($param: $ty),*) -> $fn_return {
197            $( $fn_impl )*
198        }
199    };
200}