Build Reference
How to: Basic Build
To build a contract, simply navigate to your contract crate and run the following command:
sc-meta all build
Configuring the Build
The build process is primarily configured using the build configuration file, currently named multicontract.toml
, and it is placed in the root of the contract crate.
It is also possible to configure the build process to produce more than one contract per project crate.
Contract Build Process Deep Dive
This section provides an in-depth overview for those who want to understand the system at a deeper level. If you are simply interested in building contracts, feel free to skip this section.
Building a contract is a complex process, but fortunately, it is handled transparently by the framework. We will walk through the components step by step and provide explanations for this architecture.
a. The Smart Contract Itself
The smart contract is defined as a trait without an implementation. This design allows the contract to execute on various platforms. Some of the implementation is generated automatically by the [klever_sc::contract]
macro.
However, not everything can be accomplished here. Notably, macros cannot access data from other modules or crates; all processing is confined to the current contract or module. Therefore, we need another mechanism to work with the complete contract data.
b. The (Generated) ABI Generator
ABIs consist of metadata about the contract. To build an ABI, we also need data from the modules. The module macros cannot be called from the contract macros (macros are executed at compilation, and we cannot guarantee that modules will need to be recompiled). However, modules can be called. This is why we generate ABI generator functions for each module, which can call each other to obtain the complete picture.
Note: The ABI generator is implemented as a trait called ContractAbiProvider.
c. Meta Crate: Generating the ABI
The next question is how to call these functions. Whenever we compile the WASM contracts, we also produce the ABIs in JSON format. Rust has something called build scripts, which are executed after compiling a project. However, these build scripts are not powerful enough for our use case.
Therefore, we decided to include an additional crate in each smart contract project. This crate is always found in the meta
folder within the contract and manages the entire build process. To minimize boilerplate, it typically contains only one line that defers execution to the framework:
fn main() {
klever_sc_meta::cli_main::<my_contract_crate::AbiProvider>();
}
To generate the ABI, it is enough to run the following command within the meta
folder:
cd meta
cargo run abi
The meta crate has access to the ABI generator because it always has a dependency on the contract crate. This is represented by my_contract_crate::AbiProvider
in the example above.
This is also the step where the meta crate parses and processes the multicontract.toml
file. If multiple outputs are specified, one ABI will be produced for each output.
d. Meta Crate: Generating wasm
Crate Code
Each contract must contain at least one wasm
crate. This crate is separate from the contract crate because it serves a different purpose: it only needs to provide the foundation for compiling WASM. Consider it an intermediary step between the contract logic and the Rust-to-WASM compiler. This separation is advantageous because it allows the smart contract crate to function as a pure Rust crate without any knowledge of WebAssembly. This simplifies testing, coverage analysis, and integration with unrelated technologies.
The wasm
crates do not add meaningful code to the smart contract. Their main task is to provide an adapter to the WASM function syntax. Specifically, they expose an external function for each desired endpoint, which delegates execution to the corresponding smart contract method.
Without proper care, there is a risk of adding unintended endpoints to the contract. For example, when a crate has multiple modules, and only one is imported into the smart contract, older versions might have included unwanted endpoints from the other modules. To prevent this, we use the ABI to generate a curated list of endpoints for each wasm
crate. This ensures that our contracts always have exactly the same endpoints as those specified in the ABIs.
This process requires code generation, and the meta
crate handles this code generation as well. Here's an example of such generated code:
// Code generated by the klever-sc multi-contract system. DO NOT EDIT.
////////////////////////////////////////////////////
////////////////// AUTO-GENERATED //////////////////
////////////////////////////////////////////////////
// Init: 1
// Endpoints: 2
// Total number of exported functions: 3
#![no_std]
#![feature(alloc_error_handler, lang_items)]
klever_sc_wasm_adapter::allocator!();
klever_sc_wasm_adapter::panic_handler!();
klever_sc_wasm_adapter::endpoints! {
adder
(
getSum
add
)
}
The macros provided by klever_sc_wasm_adapter
help keep even this generated code concise.
For multi-contract builds, one wasm
crate needs to be generated for each output contract:
- The main
wasm
crate always resides in thewasm
folder. While the source file is auto-generated, theCargo.toml
file must be provided by the developer. - Other
wasm
contracts (referred to as "secondary" contracts) are placed in folders starting withwasm-
, for example,wasm-multisig-view
. These crates are entirely generated based on data frommulticontract.toml
. TheCargo.toml
files for these crates are derived from the mainwasm
crate'sCargo.toml
. All configurations are inherited from the main crate except for the crate name. - Warning: Any folders beginning with
wasm-
that are not accounted for will be deleted without warning to maintain a clean folder structure in case of renames.
e. Meta Crate: The Actual WASM Build
The previous two steps can be executed by running cargo run
in the meta crate. To perform a build, you must use the cargo run build
command.
With the ABI information and generated code in hand, the meta crate can build all the WASM contracts, one for each output contract. The Rust compiler places the resulting files in the designated target
folder, but for convenience, the meta crate moves the executables to the project's output
folder and renames them according to the configured names.
simply calls the meta crate to perform this job. This is because, at this point, only the meta crate has access to the ABIs and can perform this task effortlessly.
f. Meta Crate: Build Post-Processing
After building the contracts, there are three more operations to perform based on the compiled WebAssembly outputs:
- All contracts are optimized using
wasm-opt
. This operation can be disabled with the--no-wasm-opt
flag. - A WAT (WebAssembly Text Format) file is generated for each contract. This feature is not enabled by default but can be turned on using the
--wat
flag. The framework simply calls thewasm2wat
tool to perform this operation. - An
.imports.json
file is generated for each contract. This feature can be disabled with the--no-imports
flag. The framework utilizes thewasm-objdump
tool to extract imports. It parses the output and saves it in JSON format.
g. Cleaning a Project
Running cargo run clean
in the meta crate will execute cargo clean
in all wasm
crates and delete the output
folder.
Build Process Summary
To summarize, the build process consists of the following steps:
- Generate code using contract/module macros, including the ABI generator.
- Invoke the ABI generator to produce an ABI in memory.
- Parse the
multicontract.toml
file (if present). - Based on the parsed configuration, determine which endpoints go to which output contracts.
- Save the ABI as JSON in the output folder (one for each output contract).
- Generate the
wasm
crates for all output contracts (all source code generated,Cargo.toml
contents copied from the mainwasm
crate). - Build all
wasm
crates. - Copy binaries from the
target
folder(s) to theoutput
folder. - Perform post-processing for each contract:
wasm-opt
,wasm2wat
, imports.
Fortunately, the framework can automate all of these steps with a single click.