| name | multiversx-wasm-debug |
| description | Analyze compiled WASM binaries for size optimization, panic analysis, and debugging with DWARF symbols. Use when troubleshooting contract deployment issues, optimizing binary size, or debugging runtime errors. |
MultiversX WASM Debugging
Analyze compiled output.wasm files for size optimization, panic investigation, and source-level debugging. This skill helps troubleshoot deployment issues and runtime errors.
When to Use
- Contract deployment fails due to size limits
- Investigating panic/trap errors at runtime
- Optimizing WASM binary size
- Understanding what's in your compiled contract
- Mapping WASM errors back to Rust source code
1. Binary Size Analysis
Using Twiggy
Twiggy analyzes WASM binaries to identify what consumes space:
cargo install twiggy
twiggy top output/my-contract.wasm
twiggy dominators output/my-contract.wasm
twiggy paths output/my-contract.wasm "function_name"
twiggy callgraph output/my-contract.wasm > graph.dot
Sample Twiggy Output
Shallow Bytes │ Shallow % │ Item
───────────────┼───────────┼─────────────────────────────────
12847 │ 18.52% │ data[0]
8291 │ 11.95% │ "function names" subsection
5738 │ 8.27% │ core::fmt::Formatter::pad
4521 │ 6.52% │ alloc::string::String::push_str
Common Size Bloat Causes
| Cause | Size Impact | Solution |
|---|
| Panic messages | High | Use sc_panic! or strip in release |
| Format strings | High | Avoid format!, use static strings |
| JSON serialization | Very High | Use binary encoding |
| Large static arrays | High | Generate at runtime or store off-chain |
| Unused dependencies | Variable | Audit Cargo.toml |
| Debug symbols | High | Build in release mode |
Size Reduction Techniques
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true
sc-meta all build --release
wasm-opt -Oz output/contract.wasm -o output/contract.opt.wasm
2. Panic Analysis
Understanding Contract Traps
When a contract traps (panics), you see:
error: execution terminated with signal: abort
Common Trap Causes
| Symptom | Likely Cause | Investigation |
|---|
unreachable | Panic without message | Check unwrap(), expect() |
out of gas | Computation limit hit | Check loops, storage access |
memory access | Buffer overflow | Check array indexing |
integer overflow | Math operation | Check arithmetic |
Finding Panics in WASM
wasm-objdump -x output/contract.wasm | grep "func\["
wasm-objdump -d output/contract.wasm | grep -B5 "unreachable"
wasm-objdump -d output/contract.wasm | grep -c "panic"
Panic Message Stripping
By default, sc_panic! includes message strings. In production:
sc_panic!("Detailed error: invalid amount {}", amount);
Or use error codes:
const ERR_INVALID_AMOUNT: u32 = 1;
const ERR_UNAUTHORIZED: u32 = 2;
if amount == 0 {
sc_panic!(ERR_INVALID_AMOUNT);
}
3. DWARF Debug Information
Building with Debug Symbols
sc-meta all build --wasm-symbols
sc-meta all build --wasm-symbols
Debug Build Output
Debug builds produce:
contract.wasm - Contract bytecode
contract.wasm.map - Source map (if available)
- Larger file size with DWARF sections
Using Debug Information
wasm-objdump --debug output/contract.wasm
wasm-objdump -h output/contract.wasm | grep "debug"
Source-Level Debugging
With debug symbols, you can:
- Map WASM instruction addresses to Rust source lines
- Set breakpoints at source locations
- Inspect variable values (in compatible debuggers)
wasmtime run --invoke function_name -g output/contract.wasm
4. WASM Structure Analysis
Examining Contract Structure
wasm-objdump -x output/contract.wasm
wasm-objdump -h output/contract.wasm
wasm-objdump -j Export -x output/contract.wasm
wasm-objdump -j Import -x output/contract.wasm
Understanding WASM Sections
| Section | Purpose | Audit Focus |
|---|
| Type | Function signatures | API surface |
| Import | VM API functions used | Capabilities |
| Function | Internal functions | Code size |
| Export | Public endpoints | Attack surface |
| Code | Actual bytecode | Logic |
| Data | Static data | Embedded secrets? |
| Name | Debug names | Information leak |
Checking Exports
wasm-objdump -j Export -x output/contract.wasm | grep "func"
5. Gas Profiling
Estimating Gas Costs
sc-meta all deploy --proxy https://devnet-gateway.multiversx.com --chain D
Identifying Gas-Heavy Code
Common gas-intensive patterns:
- Storage reads/writes
- Cryptographic operations
- Large data serialization
- Loop iterations
for item in self.large_list().iter() {
self.process(item);
}
let batch_size = 10;
for i in 0..batch_size {
let item = self.large_list().get(start_index + i);
self.process(item);
}
6. Common Debugging Scenarios
Scenario: Contract Deployment Fails
ls -la output/contract.wasm
twiggy top output/contract.wasm
Scenario: Transaction Fails with unreachable
- Check for
unwrap() calls
- Check for array index out of bounds
- Check for division by zero
- Build with debug and check DWARF info
Scenario: Gas Exceeded
sc-meta all build --wasm-symbols
Scenario: Unexpected Behavior
#[endpoint]
fn debug_function(&self, input: BigUint) {
self.debug_event(&input);
let result = self.compute(input);
self.debug_event(&result);
}
#[event("debug")]
fn debug_event(&self, value: &BigUint);
7. Tools Summary
| Tool | Purpose | Install |
|---|
twiggy | Size analysis | cargo install twiggy |
wasm-objdump | WASM inspection | Part of wabt |
wasm-opt | Size optimization | cargo install wasm-opt or part of binaryen |
wasmtime | WASM runtime/debug | cargo install wasmtime |
sc-meta | MultiversX build tool | cargo install multiversx-sc-meta |
8. Best Practices
- Always check release size before deployment
- Profile on devnet before mainnet deployment
- Use events for debugging instead of storage (cheaper)
- Strip debug info in production builds
- Monitor gas costs as contract evolves
- Keep twiggy reports to track size changes over time