Skip to main content
Version: Next

Calimero Rust SDK

This guide provides a comprehensive reference of the essential macros and functionality provided by the Calimero SDK for building P2P Rust applications.

Core Macros

#[app::state]

Marks a struct as the application state. The state struct must implement BorshSerialize and BorshDeserialize. This macro automatically generates the Self::external() method for external proposal functionality.

use calimero_sdk::borsh::{BorshDeserialize, BorshSerialize};

#[app::state(emits = for<'a> MyEvent<'a>)]
#[derive(Default, BorshSerialize, BorshDeserialize)]
#[borsh(crate = "calimero_sdk::borsh")]
struct MyAppState {
// Your state fields here
users: calimero_storage::collections::UnorderedMap<String, UserProfile>,
}

Key Features:

  • Automatically generates Self::external() method for proposal management
  • Integrates with Calimero's storage system
  • Supports event emission specification

#[app::logic]

Marks an implementation block as containing the application logic. All public methods in this block become available as application endpoints.

#[app::logic]
impl MyAppState {
// Your methods here
pub fn add_user(&mut self, username: String, profile: UserProfile) -> app::Result<()> {
// Implementation
Ok(())
}
}

#[app::init]

Marks a method as the initializer, which is called when the application is first deployed.

#[app::logic]
impl MyAppState {
#[app::init]
pub fn init() -> Self {
Self {
users: calimero_storage::collections::UnorderedMap::new(),
}
}

// Example with init parameters
#[app::init]
pub fn init_with_params(
initial_admin: String,
max_users: u32,
is_public: bool,
) -> Self {
Self {
users: calimero_storage::collections::UnorderedMap::new(),
admin: initial_admin,
max_users,
is_public,
}
}
}

#[app::event]

Defines an event type that can be emitted by your application. Events support lifetime parameters for efficient string handling.

#[app::event]
pub enum MyEvent<'a> {
ValueUpdated { key: &'a str, value: &'a str },
ValueRemoved { key: &'a str },
UserAdded { username: &'a str },
}

Utility Macros

app::emit!

Emit events from your application. Events are only emitted if the transaction succeeds.

app::emit!(MyEvent::ValueUpdated {
key: &key,
value: &new_value
});

app::emit!(MyEvent::UserAdded { username: &username });

app::log!

Log messages for debugging and monitoring. These appear in the application logs.

app::log!("Setting key: {:?} to value: {:?}", key, value);
app::log!("User {} added successfully", username);

app::bail!

Return an error and exit the current function early.

if !self.users.contains_key(&username) {
app::bail!("User not found: {}", username);
}

app::err!

Create an error value for returning from functions.

return app::err!("Invalid input: {}", input);

Return Types

The SDK provides a convenient app::Result<T> type alias:

use calimero_sdk::app;

pub fn my_function(&self) -> app::Result<String> {
// Your implementation
Ok("success".to_string())
}

Complete Example

Here's a complete example showing how these macros work together:

use calimero_sdk::borsh::{BorshDeserialize, BorshSerialize};
use calimero_sdk::{app, env};
use calimero_storage::collections::UnorderedMap;

#[app::event]
pub enum StoreEvent<'a> {
ValueSet { key: &'a str, value: &'a str },
ValueRemoved { key: &'a str },
}

#[app::state(emits = for<'a> StoreEvent<'a>)]
#[derive(Default, BorshSerialize, BorshDeserialize)]
#[borsh(crate = "calimero_sdk::borsh")]
struct Store {
values: UnorderedMap<String, String>,
}

#[app::logic]
impl Store {
#[app::init]
pub fn init() -> Self {
Self {
values: UnorderedMap::new(),
}
}

pub fn set(&mut self, key: String, value: String) -> app::Result<()> {
app::log!("Setting key: {:?} to value: {:?}", key, value);

if self.values.contains(&key)? {
app::emit!(StoreEvent::ValueSet {
key: &key,
value: &value
});
} else {
app::emit!(StoreEvent::ValueSet {
key: &key,
value: &value
});
}

self.values.insert(key, value)?;
Ok(())
}

pub fn get(&self, key: &str) -> app::Result<Option<String>> {
app::log!("Getting key: {:?}", key);
self.values.get(key).map_err(Into::into)
}

pub fn remove(&mut self, key: &str) -> app::Result<Option<String>> {
app::log!("Removing key: {:?}", key);

if let Some(value) = self.values.remove(key)? {
app::emit!(StoreEvent::ValueRemoved { key });
Ok(Some(value))
} else {
Ok(None)
}
}
}

Important Notes

  1. State changes are atomic - if a method fails, all changes are rolled back

  2. Events are only emitted if the transaction succeeds
  3. Read-only operations have no network overhead
  4. All public methods in the #[app::logic] block become available as application endpoints

  5. Collections return Result<T, StoreError> for most operations - handle errors appropriately

  6. The Self::external() method is automatically generated by #[app::state] for proposal management

Next Steps

Was this page helpful?
Need some help? Check Support page