Skip to content

Conversation

@leighmcculloch
Copy link
Member

@leighmcculloch leighmcculloch commented Jan 8, 2026

What

Filter unused type and event definitions from the contractspecv0 WASM section during stellar contract build using markers embedded by the SDK.

Changes:

  • Add new filter module in soroban-spec-tools with marker extraction and filtering
  • Add compute_marker_hash(), extract_spec_markers(), and filter_by_markers() functions
  • Add filtered_spec_xdr_with_markers() method to Spec
  • Add replace_custom_section() function for WASM manipulation
  • Replace wasm-gen with wasm-encoder to enable section replacement (not just append)
  • Integrate filtering into build process after cargo build completes

Why

Contract WASM files can contain unused type and event definitions in the contractspecv0 custom section that inflate binary size unnecessarily.

The problem: Any exported contracttypes or contractevents defined in the SDK or any library that don't get used in the importing contract still end up in the contract spec. This results in awkward behaviour where we avoid exporting contracttypes in the SDK, even though doing so would be convenient and enable using contracttypes more frequently there.

Why the SDK can't fix this alone: Because of how proc-macros run in isolation and cannot coordinate in WASM builds in Rust (the ctor crate cannot be used like we use it in non-wasm builds in tests), there's nothing the soroban-sdk can do to identify whether a contracttype that sometimes needs to be exported hasn't been used and can be excluded.

Solution: The SDK now embeds markers in the regular data section for each type/event that is actually used. These markers survive dead code elimination only if the corresponding code is reachable. The CLI extracts these markers and filters the spec accordingly.

Examples of unused entries that get filtered:

  • Error enums from imported libraries that are never used by any function
  • Types that are defined but never referenced in reachable code
  • Events that are defined but never published
  • Transitive type dependencies that no longer apply after refactoring

Close #2030
Close stellar/rs-soroban-sdk#1569
Close stellar/rs-soroban-sdk#1330

How

The filtering uses markers embedded by the SDK:

Marker format (12 bytes):

  • Bytes 0-3: SpEc magic prefix (alternating case to avoid false positives)
  • Bytes 4-11: 64-bit truncated SHA256 hash of the spec entry XDR bytes

Filtering process:

  1. Parse the WASM after cargo build completes
  2. Scan the data section for markers (looking for SpEc magic bytes)
  3. Extract the 8-byte hash from each marker found
  4. For each spec entry in contractspecv0:
    • Functions: always kept (they define the contract's API)
    • Types/events: compute hash of entry's XDR, keep only if hash exists in markers
  5. Replace the contractspecv0 custom section with the filtered version

Why this works: The SDK's proc-macros generate code that references a static marker when a type/event is used. If that code path survives dead code elimination (DCE), the marker remains in the data section. If the type/event is never used, DCE removes the code and the marker disappears.

WASM manipulation: Previously the CLI only modified WASM in an append fashion. This PR uses wasm-encoder with RawSection to copy sections verbatim and replace only the target custom section, enabling true section replacement.

SDK Support Required

This feature requires SDK support for embedding markers. See: stellar/rs-soroban-sdk#1571

Try It Out

Install the modified stellar-cli that strips unused specs:

cargo install --locked --git https://github.com/stellar/stellar-cli --branch spec-clean stellar-cli

Import the modified soroban-sdk that marks used specs:

[dependencies]
soroban-sdk = { git = "https://github.com/stellar/rs-soroban-sdk", branch = "spec-markers" }

[dev-dependencies]
soroban-sdk = { git = "https://github.com/stellar/rs-soroban-sdk", branch = "spec-markers", features = ["testutils"] }"

Build using the stellar-cli:

stellar contract build

Inspect the contract interface and it should only show types actually used at the boundary of the contract (inputs, outputs, and events):

stellar contract info interface --wasm target/wasm32v1-none/release/contract.wasm

@github-project-automation github-project-automation bot moved this to Backlog (Not Ready) in DevX Jan 8, 2026
@leighmcculloch leighmcculloch requested a review from Copilot January 8, 2026 15:05
@socket-security
Copy link

socket-security bot commented Jan 8, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​wasm-encoder@​0.235.08210093100100
Addedcargo/​wasmparser@​0.235.08210093100100

View full report

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds automatic filtering of unused types from contract specifications during the build process. It implements reachability analysis using fixed-point iteration to identify and preserve only types that are transitively referenced by contract functions, while always preserving functions and events. The implementation replaces wasm-gen with wasm-encoder for more flexible WASM manipulation capabilities.

Key changes:

  • Implements a new filter module with reachability analysis to detect unused UDTs
  • Refactors WASM manipulation to use wasm-encoder instead of wasm-gen, enabling section replacement
  • Integrates filtering into the contract build pipeline after metadata injection

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
cmd/soroban-cli/src/commands/contract/build.rs Adds filter_spec call to build pipeline; refactors inject_meta to use wasm-encoder; adds new error types
cmd/soroban-cli/Cargo.toml Replaces wasm-gen dependency with wasm-encoder 0.235.0
cmd/crates/soroban-spec-tools/src/lib.rs Exports new filter module
cmd/crates/soroban-spec-tools/src/filter.rs Implements reachability analysis and filtering logic with comprehensive unit tests
cmd/crates/soroban-spec-tools/src/contract.rs Adds filter_unused_types, filtered_spec_xdr methods and replace_custom_section utility; includes tests
cmd/crates/soroban-spec-tools/Cargo.toml Adds wasm-encoder 0.235.0 dependency
Cargo.lock Updates dependencies with wasm-encoder and wasmparser 0.235.0; removes obsolete wasm-gen dependencies

@leighmcculloch leighmcculloch changed the title Strip unused types from contract spec during build Strip unused types/events/errors from contract spec during build Jan 9, 2026
@leighmcculloch leighmcculloch changed the title Strip unused types/events/errors from contract spec during build Add post-build stripping of unused types/events/errors from contract spec Jan 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog (Not Ready)

2 participants