This document describes the WASM binary size optimization strategy for the Healthy-Stellar contracts repository. Soroban contracts are charged storage fees proportional to their on-chain footprint, making binary size optimization critical for cost efficiency.
Target Size Limit: 200 KB per contract (optimized)
The 200 KB limit is based on:
- Stellar Network Storage Costs: Smaller binaries reduce deployment and hosting costs
- Transaction Size Limits: Deployment transactions have size constraints
- Best Practices: Industry standard for smart contract efficiency
- Performance: Smaller binaries load and execute faster
- Network Efficiency: Reduces bandwidth requirements
Run make measure-sizes to generate a current size report for all contracts.
Primary optimization tool with aggressive size reduction.
Installation:
# macOS
brew install binaryen
# Linux
apt-get install binaryen
# Or download from: https://github.com/WebAssembly/binaryen/releasesUsage:
wasm-opt -O4 -o output.wasm input.wasmOptimization Levels:
-O0: No optimization-O1: Basic optimization-O2: More optimization-O3: Aggressive optimization-O4: Maximum optimization (size-focused)-Oz: Optimize for size
Soroban's built-in optimization tool.
Installation:
cargo install soroban-cli --features optUsage:
soroban contract optimize --wasm input.wasm --wasm-out output.wasmRemoves debug symbols and metadata.
Installation:
# Part of WABT toolkit
# macOS
brew install wabt
# Linux
apt-get install wabtUsage:
wasm-strip input.wasm -o output.wasmCode size profiler for identifying large functions.
Installation:
cargo install twiggyUsage:
# Show top size contributors
twiggy top input.wasm
# Show dominator tree
twiggy dominators input.wasm
# Show paths to a function
twiggy paths input.wasm# Build WASM binaries
make build-wasm
# Optimize all binaries
make optimize
# Measure sizes
make measure-sizes
# Check against limits (CI)
make check-sizes
# Profile binaries
make profile-wasm
# Strip debug symbols
make strip-wasmAdd to Cargo.toml:
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Enable Link Time Optimization
codegen-units = 1 # Better optimization (slower compile)
strip = true # Strip symbols
panic = "abort" # Smaller panic handler
overflow-checks = false # Disable overflow checks (use carefully)Review Dependencies:
# Show dependency tree
cargo tree
# Find unused dependencies
cargo install cargo-udeps
cargo +nightly udepsOptimize Dependencies:
- Use feature flags to include only needed functionality
- Replace heavy dependencies with lighter alternatives
- Avoid dependencies with large transitive dependency trees
Example:
[dependencies]
# Instead of full serde
serde = { version = "1.0", default-features = false, features = ["derive"] }
# Use soroban-sdk types instead of external types
soroban-sdk = "23.0.0"Extract Common Code:
- Create shared libraries for common functionality
- Avoid code duplication across contracts
- Use workspace dependencies
Example Structure:
contracts/
shared/ # Shared utilities
contract-a/ # Uses shared
contract-b/ # Uses shared
Conditional Compilation:
#[cfg(not(test))]
fn test_only_function() {
// Only compiled in tests
}
#[cfg(feature = "debug")]
fn debug_function() {
// Only with debug feature
}Remove Unused Imports:
cargo clippy -- -W unused-importsUse Efficient Types:
// Instead of String
use soroban_sdk::String;
// Instead of Vec
use soroban_sdk::Vec;
// Use compact representations
use u32 instead of u64 where possibleStrategic Inlining:
#[inline(always)]
fn small_hot_function() {
// Frequently called small function
}
#[inline(never)]
fn large_cold_function() {
// Rarely called large function
}Avoid Heavy Macros:
- Macros can generate significant code
- Use functions instead where possible
- Be selective with derive macros
Compact Error Types:
#[contracterror]
#[repr(u32)]
pub enum Error {
NotFound = 1,
Unauthorized = 2,
// Use u32 error codes instead of strings
}make build-wasm
make measure-sizesThis generates:
wasm-size-report.md- Detailed size reportwasm-sizes.csv- CSV data for analysis
make profile-wasmThis generates:
wasm-profiles/profile-summary.md- Overviewwasm-profiles/*_top.txt- Top size contributorswasm-profiles/*_dominators.txt- Dominator analysiswasm-profiles/*_paths.txt- Call pathswasm-profiles/size-comparison.txt- Visual comparison
Review profile data to find:
- Large functions that can be optimized
- Duplicated code across contracts
- Heavy dependencies
- Unused code
Implement optimization strategies based on profile data.
make measure-sizesCompare before/after sizes to verify improvements.
The wasm-size-check.yml workflow runs on every PR:
- Builds all contracts
- Optimizes binaries
- Checks sizes against 200 KB limit
- Fails CI if any contract exceeds limit
- Posts size report as PR comment
- Uploads size report as artifact
- PR Comments: Size report posted automatically
- GitHub Actions: Check "WASM Size Check" workflow
- Artifacts: Download detailed reports
Steps to resolve:
-
Profile the contract:
make profile-wasm
-
Review top contributors:
cat wasm-profiles/CONTRACT_NAME_top.txt
-
Check dependencies:
cargo tree -p CONTRACT_NAME
-
Apply optimizations:
- Remove unused dependencies
- Extract shared code
- Optimize data structures
- Use more aggressive compiler flags
-
Measure improvement:
make measure-sizes
Check:
- wasm-opt is installed and in PATH
- Cargo.toml has release optimizations
- No debug features enabled
- LTO is enabled
Common issues:
- Missing wasm32-unknown-unknown target
- Incompatible dependencies
- Feature flag conflicts
Solutions:
# Add WASM target
rustup target add wasm32-unknown-unknown
# Clean and rebuild
cargo clean
make build-wasm- Monitor Size Early: Check sizes during development, not just before release
- Profile Regularly: Run profiling after major changes
- Review Dependencies: Audit dependencies before adding
- Share Code: Extract common functionality to shared libraries
- Test Optimizations: Ensure optimizations don't break functionality
- Check Size Impact: Review size changes in PRs
- Question Large Additions: Investigate significant size increases
- Verify Optimization: Ensure new code follows optimization guidelines
- Document Decisions: Explain trade-offs between size and functionality
- Always Optimize: Never deploy unoptimized binaries
- Verify Sizes: Check sizes before deployment
- Document Sizes: Record deployed binary sizes
- Monitor Costs: Track storage costs over time
Before:
[dependencies]
serde = "1.0"
serde_json = "1.0"After:
[dependencies]
# Use soroban-sdk types instead
soroban-sdk = "23.0.0"Result: 50 KB reduction
Before:
- Contract A: 180 KB (includes validation logic)
- Contract B: 190 KB (includes same validation logic)
After:
- Shared library: 30 KB (validation logic)
- Contract A: 150 KB (uses shared)
- Contract B: 160 KB (uses shared)
Result: 50 KB total reduction
Before:
[profile.release]
opt-level = 3After:
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
strip = trueResult: 20-30% size reduction
- Run
make measure-sizes - Review size trends
- Identify growing contracts
- Plan optimization work
- Full profiling of all contracts
- Dependency audit
- Shared code opportunities
- Update optimization strategies
- Reduce total binary size by X%
- Bring all contracts under limit
- Improve optimization tooling
- Update best practices
# Rust and Cargo
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# WASM target
rustup target add wasm32-unknown-unknown
# Binaryen (wasm-opt)
brew install binaryen # macOS
apt-get install binaryen # Linux
# WABT (wasm-strip, wasm-objdump)
brew install wabt # macOS
apt-get install wabt # Linux
# Soroban CLI
cargo install soroban-cli --features opt
# Twiggy
cargo install twiggy
# cargo-udeps
cargo install cargo-udeps| Tool | Purpose | Command |
|---|---|---|
| wasm-opt | Optimize binary | wasm-opt -O4 -o out.wasm in.wasm |
| wasm-strip | Remove symbols | wasm-strip in.wasm -o out.wasm |
| twiggy | Profile size | twiggy top in.wasm |
| soroban | Optimize | soroban contract optimize --wasm in.wasm |
| cargo-udeps | Find unused deps | cargo +nightly udeps |
- Soroban Documentation
- Binaryen GitHub
- WABT GitHub
- Twiggy GitHub
- Rust WASM Book
- Cargo Profile Documentation
For questions or issues:
- Check this documentation
- Review profile data
- Consult team members
- Create an issue with profile data attached
Remember: Binary size optimization is an ongoing process. Regular monitoring and proactive optimization prevent size creep and keep deployment costs low.