Erst is a premium developer toolset for the Stellar network, designed to provide high-fidelity "glass-box" debugging and simulation for Soroban smart contracts.
Status: Active Development (Phase 4: Advanced Diagnostics) Documentation: https://dotandev-hintents-75.mintlify.app/ Focus: High-Fidelity Simulation, Auth Tracing, and Security Auditing
The primary goal of erst is to eliminate the opaque "black box" experience of failed Stellar smart contract transactions. By providing local-first, high-fidelity replay and tracing, erst maps generic network errors back to human-readable diagnostic events and source code.
- Go (1.25.0 or later)
- Rust (1.87 or later, with Cargo)
# Clone the repository
git clone https://github.com/dotandev/hintents.git
cd hintents
# Build the Rust simulator
cd simulator && cargo build && cd ..
# Build the Go CLI
go build -o erst ./cmd/erstNote: We will provide pre-compiled binaries via Homebrew and direct downloads in future releases.
Core Features (Planned):
- Transaction Replay: Fetch a failed transaction's envelope and ledger state from an RPC provider.
- Local Simulation: Re-execute the transaction logically in a local environment.
- Trace decoding: Map execution steps and failures back to readable instructions or Rust source lines.
- Source Mapping: Map WASM instruction failures to specific Rust source code lines using debug symbols.
- GitHub Source Links: Automatically generate clickable GitHub links to source code locations in traces (when in a Git repository).
- Error Suggestions: Heuristic-based engine that suggests potential fixes for common Soroban errors.
Fetches a transaction envelope from the Stellar Public network and prints its XDR size (Simulation pending).
./erst debug <transaction-hash> --network testnetDebug an offline envelope from stdin (no RPC):
./erst debug < tx.xdrLaunch an interactive terminal UI to explore transaction execution traces with search functionality.
./erst debug <transaction-hash> --interactive
# or
./erst debug <transaction-hash> -iFeatures:
- Search: Press
/to search through traces (contract IDs, functions, errors) - Help overlay: Press
?orhto see all keyboard shortcuts - Tree Navigation: Expand/collapse nodes, navigate with arrow keys
- Syntax Highlighting: Color-coded contract IDs, functions, and errors
- Fast Navigation: Jump between search matches with
n/N - Match Counter: See "Match 2 of 5" status while searching
See internal/trace/README.md for detailed documentation.
Generate interactive flamegraphs to visualize CPU and memory consumption during contract execution:
./erst debug --profile <transaction-hash>This generates an interactive HTML file (<tx-hash>.flamegraph.html) with:
- Hover tooltips showing frame details (function name, duration, percentage)
- Click-to-zoom to focus on specific call stacks
- Search/highlight to find frames by name
- Dark mode support that adapts to your system theme
Export Formats:
# Interactive HTML (default)
./erst debug --profile --profile-format html <transaction-hash>
# Raw SVG with dark mode support
./erst debug --profile --profile-format svg <transaction-hash>See docs/INTERACTIVE_FLAMEGRAPH.md for detailed documentation and docs/examples/sample_flamegraph.html for a live demo.
erst includes a small utility command to generate a deterministic, signed audit log from a JSON payload.
Provide a PKCS#8 PEM Ed25519 private key via env or CLI:
- Env:
ERST_AUDIT_PRIVATE_KEY_PEM - CLI:
--software-private-key <pem>
Example:
node dist/index.js audit:sign \
--payload '{"input":{},"state":{},"events":[],"timestamp":"2026-01-01T00:00:00.000Z"}' \
--software-private-key "$(cat ./ed25519-private-key.pem)"Select the PKCS#11 provider with --hsm-provider pkcs11 and configure the module/token/key via env vars.
Required env vars:
ERST_PKCS11_MODULE(path to the PKCS#11 module.so)ERST_PKCS11_PINERST_PKCS11_KEY_LABELorERST_PKCS11_KEY_ID(hex)ERST_PKCS11_PUBLIC_KEY_PEM(SPKI PEM public key for verification/audit metadata)
Optional:
ERST_PKCS11_SLOT(numeric index into the slot list)ERST_PKCS11_TOKEN_LABELERST_PKCS11_PIV_SLOT(YubiKey PIV slot such as 9a, 9c, 9d, 9e, 82-95, f9)
Example:
export ERST_PKCS11_MODULE=/usr/lib/softhsm/libsofthsm2.so
export ERST_PKCS11_PIN=1234
export ERST_PKCS11_KEY_LABEL=erst-audit-ed25519
export ERST_PKCS11_PUBLIC_KEY_PEM="$(cat ./ed25519-public-key-spki.pem)"
node dist/index.js audit:sign \
--hsm-provider pkcs11 \
--payload '{"input":{},"state":{},"events":[],"timestamp":"2026-01-01T00:00:00.000Z"}'The command prints the signed audit log JSON to stdout so it can be redirected to a file.
For YubiKey PIV (YKCS11) usage details, see docs/pkcs11-yubikey.md.
Erst registers a custom erst:// URI scheme, allowing external tools (browsers, dashboards) to deep-link directly into a debug session.
Register the protocol handler:
./erst protocol:registerOpen a debug session via URI:
./erst protocol:handle "erst://debug/<transaction-hash>?network=testnet"With an optional operation index:
./erst protocol:handle "erst://debug/<transaction-hash>?network=mainnet&op=0"Unregister the handler when no longer needed:
./erst protocol:unregister- Architecture Overview: Deep dive into how the Go CLI communicates with the Rust simulator, including data flow, IPC mechanisms, and design decisions.
- Project Proposal: Detailed project proposal and roadmap.
- Source Mapping: Implementation details for mapping WASM failures to Rust source code.
- Debug Symbols Guide: How to compile Soroban contracts with debug symbols.
- Error Suggestions: Heuristic-based error suggestion engine for common Soroban errors.
- Canonical JSON Serialization: Deterministic JSON serialization for audit log hashing.
- Interactive Trace Showcase: Try out the interactive trace explorer online.
- Time Travel Guide: How to use Magic Rewind to replay transactions across time, save sessions to disk, and share reproducible debug files.
Stellar's soroban-env-host executes WASM. When it traps (crashes), the specific reason is often sanitized or lost in the XDR result to keep the ledger size small.
erst operates by:
- Fetching Data: Using the Stellar RPC to get the
TransactionEnvelopeandLedgerFootprint(read/write set) for the block where the tx failed. - Simulation Environment: A Rust binary (
erst-sim) that integrates withsoroban-env-hostto replay transactions. - Execution: Feeding the inputs into the VM and capturing
diagnostic_events.
For a detailed explanation of the architecture, see docs/architecture.md.
We are building this open-source to help the entire Stellar community. All contributions, from bug reports to new features, are welcome. Please follow our guidelines to ensure code quality and consistency.
- Go 1.24.0+
- Rust 1.70+ (for building the simulator binary)
- Stellar CLI (for comparing results)
make(for running standard development tasks)
-
Clone the repo:
git clone https://github.com/dotandev/hintents.git cd hintents -
Install dependencies:
go mod download cd simulator && cargo fetch && cd ..
-
Build the Rust simulator:
cd simulator cargo build --release cd ..
-
Run tests:
go test ./... cd simulator && cargo test --verbose
This project enforces strict linting rules to maintain code quality. See docs/STRICT_LINTING.md for details.
Quick commands:
# Run all strict linting (Go + Rust)
make lint-all-strict
# Go linting only
make lint-strict
# Rust linting only
make rust-lint-strict
# Install pre-commit hooks
pip install pre-commit && pre-commit installThe CI pipeline fails immediately on:
- Unused variables, imports, or functions
- Dead code
- Any linting warnings
- Formatting: Run
go fmt ./...before committing - Linting: Must pass
golangci-lintwithout errors:golangci-lint run ./...
- Naming Conventions:
- Use
PascalCasefor exported identifiers (types, functions, constants) - Use
camelCasefor unexported identifiers - Use
UPPER_SNAKE_CASEfor constants - Interface names should end with
-er:Reader,Writer,Logger
- Use
- Error Handling:
- Always check and handle errors explicitly
- Wrap errors with context using
fmt.Errorf:fmt.Errorf("operation failed: %w", err) - Never use bare
panic()in production code
- Documentation:
- All exported functions and types must have documentation comments
- Comments should be complete sentences starting with the name
- Example:
// Logger provides structured logging for diagnostic events.
- Formatting: Run
cargo fmt --allbefore committing - Linting: Must pass
cargo clippy:cargo clippy --all-targets --release -- -D warnings
- Naming Conventions:
- Use
snake_casefor functions and variables - Use
PascalCasefor types and traits - Use
UPPER_SNAKE_CASEfor constants
- Use
- Error Handling:
- Prefer
Result<T, E>over panics - Use custom error types for domain-specific errors
- Avoid unwrapping in production code except for obvious invariants
- Prefer
- Documentation:
- Document all public functions with doc comments (
///) - Include examples for complex functions
- Use
cargo doc --opento review generated documentation
- Document all public functions with doc comments (
Follow the Conventional Commits specification:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: A new featurefix: A bug fixtest: Adding or improving testsdocs: Documentation changesrefactor: Code refactoring without feature changesperf: Performance improvementschore: Build, CI, or dependency updatesci: CI/CD configuration changes
Scopes: Use specific areas like sim, cli, updater, trace, analyzer, etc.
Examples:
feat(sim): Add protocol version spoofing for harness
test(sim): Add 1000+ transaction regression suite
fix(updater): Handle network timeouts gracefully
docs: Add comprehensive contribution guidelines
Rules:
- Keep subject line under 50 characters
- Use imperative mood ("add", not "added" or "adds")
- No period at the end of the subject
- Provide detailed explanation in the body if the change is non-obvious
- Reference related issues:
Closes #350, refs #343
- Title: Follow commit message convention (this becomes the squashed commit)
- Description:
- Brief summary of changes
- Link to related issues:
Closes #XXX - Explain the "why" behind the changes
- Highlight any breaking changes
- PR Checks:
- All CI checks must pass
- Code coverage must not decrease
- All tests must pass locally before submitting
- Format:
## Description Brief explanation of the changes. ## Related Issues Closes #350, relates to #343 ## Testing How was this tested? Include specific test cases. ## Checklist - [ ] Code follows style guidelines - [ ] Tests added/updated - [ ] Documentation updated - [ ] No new warnings or errors
- Unit Tests: All new functions must have unit tests
- Coverage: Aim for 80%+ coverage. Critical paths should have 90%+ coverage
- Integration Tests: Include tests that verify feature interactions
- Running Tests:
# Go tests go test -v -race ./... go test -v -race -cover ./... # Rust tests cargo test --all cargo test --all --release
- Bench Tests: For performance-critical code, include benchmarks
go test -bench=. -benchmem ./...
-
Create a branch:
git checkout -b feat/my-feature # or for bug fixes: git checkout -b fix/issue-description -
Make changes and test locally:
go test ./... go fmt ./... golangci-lint run ./... cargo clippy --all-targets -- -D warnings cargo fmt --all -
Commit with conventional messages:
git add . git commit -m "feat(scope): description"
-
Push and create PR:
git push origin feat/my-feature # Then create PR on GitHub with detailed description -
Address feedback:
- Make requested changes
- Commit with descriptive messages
- Force-push if necessary:
git push -f origin feat/my-feature
Run the provided scripts before submitting:
# Format Go code
go fmt ./...
# Run linters
golangci-lint run ./...
# Format Rust code
cargo fmt --all
# Check Rust with clippy
cargo clippy --all-targets --release -- -D warnings
# Run all checks
make lint
make formatSee docs/proposal.md for the detailed proposal.
- Phase 1: Research RPC endpoints for fetching historical ledger keys.
- Phase 2: Build a basic "Replay Harness" that can execute a loaded WASM file.
- Phase 3: Connect the harness to live mainnet data.
- Phase 4: Advanced Diagnostics & Source Mapping (Current Focus).
go test -run TestName ./packagego test -cpuprofile=cpu.prof -memprofile=mem.prof ./...
go tool pprof cpu.profGOOS=linux GOARCH=amd64 go build -o erst-linux-amd64 ./cmd/erstgo clean
cargo clean
make cleanWhen reviewing PRs, ensure:
- Code follows naming and style conventions
- Error handling is appropriate
- Tests are adequate and pass
- Documentation is clear and complete
- No unnecessary dependencies added
- Performance implications considered
- Security implications reviewed
- Commit messages follow convention
- Questions? Open a GitHub Discussion
- Found a bug? Create an Issue with reproduction steps
- Have an idea? Start a Discussion before implementing
- Documentation issue? Create an Issue with details
- No Emojis: Commit messages and PR titles should not contain emojis
- No "Slops": Avoid vague language like "fixes stuff" or "updates things"
- Clear Messages: Every commit should have a clear, descriptive message
- Lint-Free: Only suppress linting errors if they are objectively false positives. Always explain suppression with
// nolint:rule-namecomments - Assume Bad Faith in Code: Write code defensively, validate inputs, handle edge cases
We welcome community contributions! Please read our CONTRIBUTING.md for details on our code of conduct, how to set up your development environment, and the process for submitting pull requests to us.
If you discover a security vulnerability, please refer to our SECURITY.md for instructions on responsible disclosure.
Thanks goes to these wonderful people:
dotdev. Code Documentation Ideas & Planning |
This project follows the all-contributors specification. Contributions of any kind welcome!
Erst is an open-source initiative. Contributions, PRs, and Issues are welcome.