skills/multiversx/mx-ai-skills/multiversx-static-analysis

multiversx-static-analysis

SKILL.md

MultiversX Static Analysis

Comprehensive static analysis guide for MultiversX codebases, covering both Rust smart contracts (multiversx-sc) and Go protocol code (mx-chain-go). This skill provides grep patterns, manual review techniques, and tool recommendations.

When to Use

  • Starting security code reviews
  • Setting up automated vulnerability scanning
  • Creating analysis checklists for audits
  • Training new security reviewers
  • Investigating specific vulnerability classes

1. Rust Smart Contracts (multiversx-sc)

Critical Grep Patterns

Unsafe Code

# Unsafe blocks - valid only for FFI or specific optimizations
grep -rn "unsafe" src/

# Generally forbidden in smart contracts unless justified

Risk: Memory corruption, undefined behavior Action: Require justification for each unsafe block

Panic Inducers

# Direct unwrap - can panic
grep -rn "\.unwrap()" src/

# Expect - also panics
grep -rn "\.expect(" src/

# Index access - can panic on out of bounds
grep -rn "\[.*\]" src/ | grep -v "storage_mapper"

Risk: Contract halts, potential DoS Action: Replace with unwrap_or_else(|| sc_panic!(...)) or proper error handling

Floating Point Arithmetic

# f32 type
grep -rn "f32" src/

# f64 type
grep -rn "f64" src/

# Float casts
grep -rn "as f32\|as f64" src/

Risk: Non-deterministic behavior, consensus failure Action: Use BigUint/BigInt for all calculations

Unchecked Arithmetic

# Direct arithmetic operators
grep -rn "[^_a-zA-Z]\+ [^_a-zA-Z]" src/  # Addition
grep -rn "[^_a-zA-Z]\- [^_a-zA-Z]" src/  # Subtraction
grep -rn "[^_a-zA-Z]\* [^_a-zA-Z]" src/  # Multiplication

# Without checked variants
grep -rn "checked_add\|checked_sub\|checked_mul" src/

Risk: Integer overflow/underflow Action: Use BigUint or checked arithmetic for all financial calculations

Map Iteration (DoS Risk)

# Iterating storage mappers
grep -rn "\.iter()" src/

# Especially dangerous patterns
grep -rn "for.*in.*\.iter()" src/
grep -rn "\.collect()" src/

Risk: Gas exhaustion DoS Action: Add pagination or bounds checking

Logical Pattern Analysis (Manual Review)

Token ID Validation

Search for payment handling:

grep -rn "call_value()" src/
grep -rn "all_esdt_transfers" src/
grep -rn "single_esdt" src/

For each occurrence, verify:

  • Token ID checked against expected value
  • Token nonce validated (for NFT/SFT)
  • Amount validated (non-zero, within bounds)
// VULNERABLE
#[payable("*")]
fn deposit(&self) {
    let payment = self.call_value().single_esdt();
    self.balances().update(|b| *b += payment.amount);
    // No token ID check! Accepts any token
}

// SECURE
#[payable("*")]
fn deposit(&self) {
    let payment = self.call_value().single_esdt();
    require!(
        payment.token_identifier == self.accepted_token().get(),
        "Wrong token"
    );
    require!(payment.amount > 0, "Zero amount");
    self.balances().update(|b| *b += payment.amount);
}

Callback State Assumptions

Search for callbacks:

grep -rn "#\[callback\]" src/

For each callback, verify:

  • Does NOT assume async call succeeded
  • Handles error case explicitly
  • Reverts state changes on failure if needed
// VULNERABLE - assumes success
#[callback]
fn on_transfer(&self) {
    self.transfer_count().update(|c| *c += 1);
}

// SECURE - handles both cases
#[callback]
fn on_transfer(&self, #[call_result] result: ManagedAsyncCallResult<()>) {
    match result {
        ManagedAsyncCallResult::Ok(_) => {
            self.transfer_count().update(|c| *c += 1);
        },
        ManagedAsyncCallResult::Err(_) => {
            // Handle failure - funds returned automatically
        }
    }
}

Access Control

Search for endpoints:

grep -rn "#\[endpoint\]" src/
grep -rn "#\[only_owner\]" src/

For each endpoint, verify:

  • Appropriate access control applied
  • Sensitive operations restricted
  • Admin functions documented
// VULNERABLE - public sensitive function
#[endpoint]
fn set_fee(&self, new_fee: BigUint) {
    self.fee().set(new_fee);
}

// SECURE - restricted
#[only_owner]
#[endpoint]
fn set_fee(&self, new_fee: BigUint) {
    self.fee().set(new_fee);
}

Reentrancy (CEI Pattern)

Search for external calls:

grep -rn "\.send()\." src/
grep -rn "\.tx()" src/
grep -rn "async_call" src/

Verify Checks-Effects-Interactions pattern:

  • All checks (require!) before state changes
  • State changes before external calls
  • No state changes after external calls in same function

2. Go Protocol Code (mx-chain-go)

Concurrency Issues

Goroutine Loop Variable Capture

grep -rn "go func" *.go

Check for loop variable capture bug:

// VULNERABLE
for _, item := range items {
    go func() {
        process(item)  // item may have changed!
    }()
}

// SECURE
for _, item := range items {
    item := item  // Create local copy
    go func() {
        process(item)
    }()
}

Map Race Conditions

grep -rn "map\[" *.go | grep -v "sync.Map"

Verify maps accessed from goroutines are protected:

// VULNERABLE
var balances = make(map[string]int)
// Accessed from multiple goroutines without mutex

// SECURE
var balances = sync.Map{}
// Or use mutex protection

Determinism Issues

Map Iteration Order

grep -rn "for.*range.*map" *.go

Map iteration in Go is random. Never use for:

  • Generating hashes
  • Creating consensus data
  • Any deterministic output
// VULNERABLE - non-deterministic
func hashAccounts(accounts map[string]int) []byte {
    var data []byte
    for k, v := range accounts {  // Random order!
        data = append(data, []byte(k)...)
    }
    return hash(data)
}

// SECURE - sort keys first
func hashAccounts(accounts map[string]int) []byte {
    keys := make([]string, 0, len(accounts))
    for k := range accounts {
        keys = append(keys, k)
    }
    sort.Strings(keys)

    var data []byte
    for _, k := range keys {
        data = append(data, []byte(k)...)
    }
    return hash(data)
}

Time Functions

grep -rn "time.Now()" *.go

time.Now() is forbidden in block processing:

// VULNERABLE
func processBlock(block *Block) {
    timestamp := time.Now().Unix()  // Non-deterministic!
}

// SECURE
func processBlock(block *Block) {
    timestamp := block.Header.TimeStamp  // Deterministic
}

3. Analysis Checklist

Smart Contract Review Checklist

Access Control

  • All endpoints have appropriate access restrictions
  • Owner/admin functions use #[only_owner] or explicit checks
  • No privilege escalation paths

Payment Handling

  • Token IDs validated in all #[payable] endpoints
  • Amounts validated (non-zero, bounds)
  • NFT nonces validated where applicable

Arithmetic

  • No raw arithmetic on u64/i64 with external inputs
  • BigUint used for financial calculations
  • No floating point

State Management

  • Checks-Effects-Interactions pattern followed
  • Callbacks handle failure cases
  • Storage layout upgrade-safe

Gas & DoS

  • No unbounded iterations
  • Storage growth is bounded
  • Pagination for large data sets

Error Handling

  • No unwrap() without justification
  • Meaningful error messages
  • Consistent error handling patterns

Protocol Review Checklist

Concurrency

  • All shared state properly synchronized
  • No goroutine loop variable capture bugs
  • Channel usage is correct

Determinism

  • No map iteration for consensus data
  • No time.Now() in block processing
  • No random number generation without deterministic seed

Memory Safety

  • Bounds checking on slices
  • No nil pointer dereferences
  • Proper error handling

4. Automated Tools

Semgrep Rules

See multiversx-semgrep-creator skill for custom rule creation.

Clippy (Rust)

cargo clippy -- -D warnings

# Useful lints:
# - clippy::arithmetic_side_effects
# - clippy::indexing_slicing
# - clippy::unwrap_used

Go Vet & Staticcheck

go vet ./...
staticcheck ./...

# Race detection
go build -race

5. Vulnerability Categories Quick Reference

Category Grep Pattern Severity
Unsafe code unsafe Critical
Float arithmetic f32|f64 Critical
Panic inducers unwrap()|expect( High
Unbounded iteration \.iter() High
Missing access control #[endpoint] without #[only_owner] High
Token validation call_value() without require High
Callback assumptions #[callback] without error handling Medium
Raw arithmetic + | - | * on u64 Medium
Weekly Installs
5
GitHub Stars
10
First Seen
Jan 30, 2026
Installed on
antigravity4
openclaw2
claude-code2
codex2
gemini-cli2
cursor2