Skip to main content

Canpack

Intermediate
Motoko
Rust

Overview

Canpack is a code generation tool designed to simplify communication across canisters written in different languages. It currently supports calling a Rust crate from Motoko code. Canpack generates a separate canister for the host language, then combines the other language's code fragments that are defined across different libraries.

Canpack supports the Mops package manager.

Canpack is still in early development. Breaking changes may be introduced at any time.

Installation

Prerequisites

  • You have Node.js installed.

  • You have downloaded and installed the IC SDK.

  • You have Rust installed.

Then, install the Canpack CLI using npm:

npm install -g canpack

Using Canpack

In a project directory that contains a dfx.json and mops.toml file, you can use Canpack by first creating a new file called canpack.json with the following content:

{
    "canisters": {
        "motoko_rust": {
            "type": "rust",
            "parts": [{
                "package": "canpack-example-hello",
                "version": "^0.1"
            }]
        }
    }
}

Then, generate the required files using the command: 

canpack

In the project's dfx.json file, configure the "dependencies" for your project's Motoko canister:

{
    "canisters": {
        "my_project_backend": {
            "dependencies": ["motoko_rust"],
            "main": "src/my_project_backend/main.mo",
            "type": "motoko"
        }
    },
}

Then, to write a Motoko canister that imports Rust crates, import Rust from the motoko_rust canister:

import Rust "canister:motoko_rust";

actor {
    public composite query func hello(name: Text) : async Text {
        await Rust.canpack_example_hello(name)
    }
}

Motoko canisters can import any Rust crate that is compatible with Canpack. Canpack supports any IC Wasm-compatible crate.

Adding Canpack support to a crate

To add Canpack support to a Rust crate, export a Rust function with canpack::export!:

canpack::export! {
    pub fn canpack_example_hello(name: String) -> String {
        format!("Hello, {name}!")
    }
}

canpack::export! requires that you add canpack as a dependency in your Cargo.toml file.

Reference local data

You can also reference local data, such as methods and constants:

const WELCOME: &str = "Welcome";

fn hello(salutation: &str, name: String) -> String {
    format!("{salutation}, {name}!")
}

canpack::export! {
    pub fn canpack_example_hello(name: String) -> String {
        hello(WELCOME, name)
    }
}

Candid methods

To configure an automatically generated Candid method for a function, use the #[canpack] attribute, 

canpack::export! {
    #[canpack(composite_query, rename = "canpack_example_hello")]
    pub fn hello(name: String) -> String {
        format!("Hello, {name}!")
    }
}

Alternatively, you can manually define a Candid method through the canpack! macro:

pub fn hello(name: String) -> String {
    format!("Hello, {name}!")
}

#[macro_export]
macro_rules! canpack {
    () => {
        #[ic_cdk::query]
        #[candid::candid_method(query)]
        fn canpack_example_hello(name: String) -> String {
            $crate::hello(name)
        }
    };
}

Resources

Canpack is open to contributions and encourages you to report bugs, ask questions, or request features using the project's GitHub issues.