Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- run: rustup target add wasm32-unknown-unknown
- uses: taiki-e/install-action@nextest
- uses: taiki-e/install-action@just

- run: sudo apt install libdbus-1-dev pkg-config libudev
- name: Run cargo fmt
run: cargo fmt --all -- --check
- name: build since clippy needs contracts to be built
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- run: rustup update
- run: rustup target add wasm32-unknown-unknown
- run: sudo apt install libdbus-1-dev pkg-config libudev
- uses: taiki-e/install-action@just
- uses: taiki-e/install-action@nextest
- uses: cargo-bins/cargo-binstall@main
Expand Down
41 changes: 26 additions & 15 deletions crates/loam-sdk-macro/src/subcontract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,24 @@ fn generate_method(trait_item: &syn::TraitItem) -> Option<TokenStream> {
let sig = &method.sig;
let name = &sig.ident;
let output = &sig.output;
let self_ty = get_receiver(sig.inputs.iter().next()?)?;

let self_ty = get_receiver(sig.inputs.iter().next()?);
let is_result = is_result_type(output);
let args_without_self = get_args_without_self(&sig.inputs);
let args = &args_to_idents(&sig.inputs, self_ty.is_none());
let attrs = &method.attrs;
let return_question_mark = if is_result { Some(quote!(?)) } else { None };

let Some(self_ty) = self_ty.as_ref() else {
return Some(generate_static_method(sig, attrs, name, args));
};
if is_mutable_method(self_ty) {
Some(generate_mutable_method(
sig,
attrs,
name,
&args_without_self,
args,
return_question_mark.as_ref(),
))
} else {
Some(generate_immutable_method(
sig,
attrs,
name,
&args_without_self,
))
Some(generate_immutable_method(sig, attrs, name, args))
}
} else {
None
Expand All @@ -81,10 +77,10 @@ fn get_receiver(arg: &syn::FnArg) -> Option<&syn::Receiver> {
}
}

pub fn get_args_without_self(inputs: &Punctuated<FnArg, Token!(,)>) -> Vec<Ident> {
pub fn args_to_idents(inputs: &Punctuated<FnArg, Token!(,)>, is_static: bool) -> Vec<Ident> {
inputs
.iter()
.skip(1)
.skip(usize::from(!is_static))
.filter_map(|arg| {
if let syn::FnArg::Typed(syn::PatType { pat, .. }) = arg {
match &**pat {
Expand All @@ -101,6 +97,21 @@ pub fn get_args_without_self(inputs: &Punctuated<FnArg, Token!(,)>) -> Vec<Ident
fn is_mutable_method(receiver: &syn::Receiver) -> bool {
receiver.reference.is_some() && receiver.mutability.is_some()
}
fn generate_static_method(
sig: &Signature,
attrs: &[Attribute],
name: &Ident,
args_without_self: &[Ident],
) -> TokenStream {
let inputs = sig.inputs.iter();
let output = &sig.output;
quote! {
#(#attrs)*
fn #name(#(#inputs),*) #output {
Self::Impl::#name(#(#args_without_self),*)
}
}
}
fn generate_immutable_method(
sig: &Signature,
attrs: &[Attribute],
Expand Down Expand Up @@ -192,10 +203,10 @@ pub fn derive_contract_impl(args: TokenStream, trait_impls: Item) -> TokenStream
.filter_map(|(first, _)| all_traits.get(&format!("Is{first}")))
.flatten()
.collect::<Vec<_>>();

let contract_name = &strukt.ident;
for (first, second) in idents {
impls.extend(quote! {
impl #first for Contract {
impl #first for #contract_name {
type Impl = #second;
}
});
Expand Down
9 changes: 5 additions & 4 deletions crates/loam-sdk-macro/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn generate_methods(item: &ItemTrait) -> Vec<TokenStream> {
.filter_map(|item| {
if let syn::TraitItem::Fn(TraitItemFn { sig, attrs, .. }) = item {
let name = &sig.ident;
Some(generate_method(sig, attrs, name))
Some(generate_method(sig, attrs, name, sig.receiver().is_none()))
} else {
None
}
Expand All @@ -70,15 +70,16 @@ fn generate_method(
sig: &syn::Signature,
attrs: &[syn::Attribute],
name: &syn::Ident,
is_static: bool,
) -> TokenStream {
let output = &sig.output;
let inputs = sig.inputs.iter().skip(1);
let args_without_self = crate::subcontract::get_args_without_self(&sig.inputs);
let inputs = sig.inputs.iter().skip(usize::from(!is_static));
let args = crate::subcontract::args_to_idents(&sig.inputs, is_static);
quote! {
#(#attrs)*
pub fn #name(env: loam_sdk::soroban_sdk::Env, #(#inputs),*) #output {
loam_sdk::soroban_sdk::set_env(env);
Contract::#name(#(#args_without_self),*)
Contract::#name(#(#args),*)
}
}
}
Expand Down
50 changes: 20 additions & 30 deletions crates/loam-subcontract-core/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,46 @@ use loam_sdk::{
};

#[contracttype(export = false)]
#[derive(Default)]
pub struct Admin(Kind);
pub struct Admin(Address);

impl Default for Admin {
fn default() -> Self {
// Admin should always be initialized in the constructor
unreachable!()
}
}

fn admin_key() -> Symbol {
symbol_short!("ADMIN")
}

impl Lazy for Admin {
fn get_lazy() -> Option<Self> {
env().storage().instance().get(&admin_key())
env().storage().instance().get(&admin_key()).map(Admin)
}

fn set_lazy(self) {
env().storage().instance().set(&admin_key(), &self);
env().storage().instance().set(&admin_key(), &self.0);
}
}

/// Work around not having `Option` in `contracttype`
#[contracttype(export = false)]
#[derive(Default)]
pub enum Kind {
Address(Address),
#[default]
None,
}

impl IsCore for Admin {
fn admin_get(&self) -> Option<Address> {
match &self.0 {
Kind::Address(address) => Some(address.clone()),
Kind::None => None,
}
Some(self.0.clone())
}

fn admin_set(&mut self, new_admin: Address) {
if let Admin(Kind::Address(admin)) = &self {
admin.require_auth();
}
self.0 = Kind::Address(new_admin);
self.0.require_auth();
self.0 = new_admin;
}

fn redeploy(&self, wasm_hash: BytesN<32>) {
self.admin_get().unwrap().require_auth();
fn upgrade(&self, wasm_hash: BytesN<32>) {
self.0.require_auth();
env().deployer().update_current_contract_wasm(wasm_hash);
}

fn __constructor(&mut self, admin: Address) {
if self.admin_get().is_none() {
self.admin_set(admin);
}
fn __constructor(admin: Address) {
Self::set_lazy(Self(admin));
}
}

Expand All @@ -66,9 +56,9 @@ pub trait IsCore {
/// a different account try to become admin
fn admin_set(&mut self, new_admin: loam_sdk::soroban_sdk::Address);

/// Admin can redeploy the contract with given hash.
fn redeploy(&self, wasm_hash: loam_sdk::soroban_sdk::BytesN<32>);
/// Admin can upgrade the contract with given hash.
fn upgrade(&self, wasm_hash: loam_sdk::soroban_sdk::BytesN<32>);

/// Constructor to set the admin
fn __constructor(&mut self, admin: loam_sdk::soroban_sdk::Address);
fn __constructor(admin: loam_sdk::soroban_sdk::Address);
}
Loading