The subdirectories are various examples of compiling smart contracts. Here are some tips useful for creating your own.
This needs to be compiled as a c dynamic lib. To do so, first generate the crate
via cargo new --lib sample
. Then add the following to Cargo.toml
:
[lib]
crate-type = ["cdylib", "rlib"]
The cdylib
is needed for the wasm target. The rlib
is needed to compile
artifacts for integration tests (and benchmarking).
Note throughout this demo I will use the name hackatom
for the project.
Please replace it with the real name of your crate. I intentionally didn't use
<name>
, so you can cut and paste for a quick demo. Just update this when
making real code.
You must support the rust target wasm32-unknown-unknown
.
Check which ones you currently have installed via
rustup target list --installed
. If wasm32 is not on that list, install using
rustup target add wasm32-unknown-unknown
Go into the subdirectory, called sample
from now on:
To compile the code, run
cargo build --release --target wasm32-unknown-unknown
. The output will be in
target/wasm32-unknown-unknown/release/hackatom.wasm
You probably don't want to explicitly set the target every time, so you can just
add the following to .cargo/config
:
[alias]
wasm = "build --release --target wasm32-unknown-unknown"
And you can now just call cargo wasm
to build it, and cargo test
to run
tests.
Note Using build.target
seems to force tests to use that target as well,
remove this or find a work-around.
This discussion
and closed PR seem to suggest
this will never be done.
The size of the wasm output is critical if it is supposed to go on a blockchain. Here are some things to make it smaller.
If you want to request the compiler to make smaller binaries, you can hit a few flags (which raise compile time significantly). Try adding this custom profile to Cargo.toml:
[profile.release]
opt-level = 3
debug = false
rpath = false
lto = true
debug-assertions = false
codegen-units = 1
panic = 'unwind'
incremental = false
overflow-checks = true
IMPORTANT it is essential that codegen-units is set to 1 for deterministic builds. Otherwise, you will have a different wasm output with each compile.
After compiling your contract, take a look at the size of the original output:
du -sh target/wasm32-unknown-unknown/release/hackatom.wasm
, it is likely
around 1.5 MB. Most of that is unneeded and can easily be trimmed. The first
approach is to use wasm-pack
to build
it. This is designed for exporting small wasm builds and js bindings for the
web, but is also the most actively maintained stack for trimmed wasm builds with
rust. wasm-gc
, the older
alternative, has been deprecated for this approach. (Note you must
install wasm-pack first):
cargo wasm
du -h target/wasm32-unknown-unknown/release/hackatom.wasm
wasm-pack build
du -h pkg/hackatom_bg.wasm
A bit smaller, huh? For the sample contract, this is around 64kB, but this varies a lot contract-by-contract. If you have plenty of dependencies and this is still too big, you can do a bit of investigation of where the size comes from, and maybe change your dependencies:
cargo install twiggy
twiggy top contract.wasm | head -20
twiggy garbage contract.wasm
twiggy dominators contract.wasm | less
Look at the imports and exports:
cargo install wasm-nm
wasm-nm -e contract.wasm
wasm-nm -i contract.wasm
You can still get a bit smaller. Note the symbol names that were used in twiggy.
Well, those come from inside the wasm build. You can strip out these symbols and
other debug info, that won't be usable when running anyway. For this we use
wasm-opt
from the enscripten toolchain. This is a bunch of C++ code that
needs to be compiled, and to simplify the whole process, as well as create
reproduceable builds, we have created
cosmwasm-opt
, which contains a
Dockerfile
that you can use to run both wasm-pack
and wasm-opt
. To make
the build, just run the following in the project directory:
docker run --rm -u $(id -u):$(id -g) -v $(pwd):/code confio/cosmwasm-opt:0.4.1
du -h contract.wasm
Note that this always outputs the file as contract.wasm
, not with the name of
the project (yes, every tool chain has a different output location). For the
hackatom sample, I now get down to 52kB, an 18% improvement. This is as far as
you can minimize the input, without removing actual functionality.
While we cannot trim down the wasm code anymore, we can still reduce the size a bit for loading in blockchain transactions. We soon plan to allow gzip-ed wasm in the transactions posted to the chain, which will further reduce gas cost of the code upload. Check out this final output:
$ gzip -k contract.wasm
$ du -h contract.wasm*
52K contract.wasm
20K contract.wasm.gz
And there you have it. We have gone from 1.5MB for the naive build, to 72kB with the standard minification tooling, all the way down to 20kB with very aggressive trimming and compression. Less than 1.5% of the original size. This is indeed something you can easily fit inside a transaction.