smartdeploy_macros/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3
4/// Adds `owner_get`, `owner_set`, and `redeploy` to the contract.
5/// This way it ensures that it is redeployable by the owner.
6#[proc_macro]
7pub fn core_riff(_: TokenStream) -> TokenStream {
8    quote::quote! {
9    #[soroban_sdk::contract]
10    pub struct Contract__;
11
12    #[soroban_sdk::contractimpl]
13    impl Contract__ {
14            /// Returns the owner of the contract
15            pub fn owner_get(env: soroban_sdk::Env) -> Option<soroban_sdk::Address> {
16                env.storage().instance().get(&Self::owner_key())
17            }
18
19            /// Sets the owner of the contract. If one already set it transfers it to the new owner, if signed by owner.
20            pub fn owner_set(env: soroban_sdk::Env, new_owner: soroban_sdk::Address) {
21                Self::owner_get(env.clone()).as_ref().map(soroban_sdk::Address::require_auth);
22                env.storage().instance().set(&Self::owner_key(), &new_owner);
23            }
24
25            /// Redeploy the contract to a Wasm hash
26            pub fn redeploy(env: soroban_sdk::Env, wasm_hash: soroban_sdk::BytesN<32>) {
27                Self::owner_get(env.clone()).as_ref().map(soroban_sdk::Address::require_auth);
28                env.deployer().update_current_contract_wasm(wasm_hash);
29            }
30            fn owner_key() -> soroban_sdk::Symbol {
31                soroban_sdk::symbol_short!("OWNER")
32            }
33    }
34        }
35    .into()
36}
37
38/// Adds `dev_deploy` to the contract.
39/// This way the contract can be redeployed with the supplied bytes
40#[proc_macro]
41pub fn dev_deploy(_: TokenStream) -> TokenStream {
42    quote::quote! {
43    #[soroban_sdk::contract]
44    pub struct DevDeploy__;
45
46    #[soroban_sdk::contractimpl]
47    impl DevDeploy__ {
48        /// Redeploy the contract with the given Wasm bytes
49        pub fn dev_deploy(env: soroban_sdk::Env, wasm: soroban_sdk::Bytes) {
50            let wasm_hash = env.deployer().upload_contract_wasm(wasm);
51            env.deployer().update_current_contract_wasm(wasm_hash);
52        }
53    }
54        }
55    .into()
56}
57
58/// Generates a contract Client for a given contract.
59/// It is expected that the name should be the same as the published contract or a contract in your current workspace.
60#[proc_macro]
61pub fn import_contract_interface(tokens: TokenStream) -> TokenStream {
62    let (name, file, _) = file_and_id(&tokens);
63    quote::quote! {
64        mod #name {
65            use super::*;
66            use soroban_sdk::TryFromVal;
67            soroban_sdk::contractimport!(file = #file);
68        }
69    }
70    .into()
71}
72
73/// Generates a contract Client for a given contract.
74/// It is expected that the name should be the same as the published contract or a contract in your current workspace.
75#[proc_macro]
76pub fn import_contract(tokens: TokenStream) -> TokenStream {
77    let (name, file, Some(id)) = file_and_id(&tokens) else {
78        panic!("contract_id.txt not found")
79    };
80
81    quote::quote! {
82        mod #name {
83            use super::*;
84            use soroban_sdk::TryFromVal;
85            soroban_sdk::contractimport!(file = #file);
86
87            pub fn address(env: &Env) -> soroban_sdk::Address {
88                let bytes: soroban_sdk::BytesN<32> = soroban_sdk::Bytes::from_slice(&env, &[#(#id),*]).try_into().unwrap();
89                soroban_sdk::Address::from_contract_id(&bytes)
90            }
91
92            pub fn new(env: &Env) -> Client {
93                let contract_id =  &address(env);
94                Client::new(env, contract_id)
95            }
96        }
97    }
98    .into()
99}
100
101fn file_and_id(tokens: &TokenStream) -> (syn::Ident, String, Option<[u8; 32]>) {
102    let dir = smartdeploy_build::target_dir().expect("failed to find target_dir");
103    let wasm = smartdeploy_build::wasm_location(&tokens.to_string(), Some(&dir))
104        .expect("failed to file wasm");
105    let name =
106        syn::parse::<syn::Ident>(tokens.clone()).expect("The input must be a valid identifier");
107    let binding = wasm.canonicalize().expect("cannot canonicalize");
108    let file = binding.to_str().unwrap();
109    let id = std::fs::read_to_string(binding.parent().unwrap().join("contract_id.txt"))
110        .ok()
111        .and_then(|s| stellar_strkey::Contract::from_string(&s).ok());
112    (name, file.to_string(), id.map(|id| id.0))
113}
114
115// pub fn contract_id(env: &Env) -> Address {
116//     let binding = soroban_sdk::short_symbol!(#id);
117//     let s = binding.as_val();
118//     Address::try_from_val(&env, s).unwrap()
119// }