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}