pink_extension_macro/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use syn::{parse_macro_input, Result};
4
5mod chain_extension;
6mod contract;
7mod driver_system;
8
9/// A drop-in replacement for `ink::contract` with pink-specific feature extensions.
10///
11/// # pink-specific features
12/// - `#[pink(on_block_end)]`
13///   Marks a function as being called on each phala block has been dispatched.
14#[proc_macro_attribute]
15pub fn contract(arg: TokenStream, input: TokenStream) -> TokenStream {
16    let config = parse_macro_input!(arg as TokenStream2);
17    let module = parse_macro_input!(input as TokenStream2);
18    let module = contract::patch(module, config);
19    module.into()
20}
21
22/// Internal use only.
23#[proc_macro_attribute]
24pub fn chain_extension(_: TokenStream, input: TokenStream) -> TokenStream {
25    let input = parse_macro_input!(input as TokenStream2);
26    let output = chain_extension::patch(input);
27    output.into()
28}
29
30/// Mark an ink trait as pink's system contract. Internal use only.
31#[proc_macro_attribute]
32pub fn system(arg: TokenStream, input: TokenStream) -> TokenStream {
33    let config = parse_macro_input!(arg as TokenStream2);
34    let module = parse_macro_input!(input as TokenStream2);
35    let module = driver_system::patch(module, config, driver_system::InterfaceType::System);
36    module.into()
37}
38
39/// This procedural macro marks an ink! trait as a 'driver contract' for the Pink system.
40///
41/// # What is the driver system?
42///
43/// The driver system is a straightforward concept. In the Pink system, there is a registry mapping driver names
44/// to driver contract addresses. The System contract provides two methods to manage this registry:
45/// `System::set_driver(driver_name, contract_address)` and `System::get_driver(driver_name)`.
46///
47/// # How does this macro work?
48///
49/// When this attribute is used, it modifies the given trait to be utilized as a driver contract
50/// within the Pink system. This is achieved by adding a new type, TraitNameRef, which implements
51/// the marked trait and provides a static method `instance()` to retrieve an instance of the driver.
52///
53/// # Example
54///
55/// Below, the `SidevmOperation` trait is annotated with `#[pink::driver]`. This marks it
56/// as a driver contract, enabling it to manage SideVM deployments.
57///
58/// ```ignore
59/// #[pink::driver]
60/// #[ink::trait_definition]
61/// pub trait SidevmOperation {
62///     #[ink(message)]
63///     fn deploy(&self, code_hash: Hash) -> Result<(), DriverError>;
64///
65///     #[ink(message)]
66///     fn can_deploy(&self, contract_id: AccountId) -> bool;
67/// }
68/// ```
69///
70/// Once a trait has been defined as a driver using this macro, it can be set as a driver
71/// by invoking the `System::set_driver(driver_name, contract_address)` method.
72///
73/// # Usage
74///
75/// The actual driver can then be retrieved and its methods, defined by the trait, can be used.
76/// For instance, to start a SideVM, one would get the driver instance and call its `deploy` method:
77///
78/// ```ignore
79/// pub fn start_sidevm(code_hash: Hash) -> Result<(), system::DriverError> {
80///     let driver =
81///         SidevmOperationRef::instance().ok_or(system::Error::DriverNotFound)?;
82///     driver.deploy(code_hash)
83/// }
84/// ```
85///
86/// Here, `SidevmOperationRef::instance()` retrieves an instance of the driver contract for "SidevmOperation",
87/// and then the `deploy` method of the driver is invoked to deploy a SideVM instance.
88/// Internally, `SidevmOperationRef::instance()` retrieves the driver contract by invoking `System::get_driver("SidevmOperation")`.
89///
90/// Note: The name of the driver contract instance (e.g., "SidevmOperation") is generated by the macro.
91#[proc_macro_attribute]
92pub fn driver(arg: TokenStream, input: TokenStream) -> TokenStream {
93    let config = parse_macro_input!(arg as TokenStream2);
94    let module = parse_macro_input!(input as TokenStream2);
95    let module = driver_system::patch(module, config, driver_system::InterfaceType::Driver);
96    module.into()
97}
98
99#[cfg(not(test))]
100fn find_crate_name(origin: &str) -> Result<syn::Ident> {
101    use proc_macro2::Span;
102    use proc_macro_crate::{crate_name, FoundCrate};
103    let name = match crate_name(origin) {
104        Ok(FoundCrate::Itself) => syn::Ident::new("crate", Span::call_site()),
105        Ok(FoundCrate::Name(alias)) => syn::Ident::new(&alias, Span::call_site()),
106        Err(e) => {
107            return Err(syn::Error::new(Span::call_site(), &e));
108        }
109    };
110    Ok(name)
111}
112
113#[cfg(test)]
114fn find_crate_name(origin: &str) -> Result<syn::Ident> {
115    use heck::ToSnakeCase;
116    use proc_macro2::Span;
117    Ok(syn::Ident::new(&origin.to_snake_case(), Span::call_site()))
118}