Offline-first CLI & Rust toolkit for Stellar/Soroban.
Inspect, decode, analyze, audit, diff, build, and deploy smart contracts with upgrade safety.
Website: sabolabs.github.io/soroban-devkit — landing +
in-browser WASM inspector
(contract bytes stay in the tab). Source: website/.
sdkt is an offline-first toolkit for inspecting, analyzing, validating, and managing Soroban smart contracts. It consolidates contract inspection, XDR decoding, storage TTL analysis, static security analysis, WASM diffing, and multi-contract deployment orchestration into a single CLI — so developers stop juggling 5+ separate tools.
Developing on Soroban often requires context-switching across multiple CLI tools and manual RPC scripts to build, audit, and deploy contracts. sdkt solves this by providing a unified interface that emphasizes offline-first analysis, upgrade safety, and deployment orchestration.
sdkt spans the full read-only and mutating contract lifecycle:
- Inspect & decode — base64 XDR decoding, contract ABI + storage inspection, event exploration.
- Analyze — storage TTL / rent visibility, Instance / Persistent / Temporary classification, offline ABI/function/event/type WASM diffing.
- Secure — static analysis of contract source (
AUTH-001/002/003/004,MOVE-001) and an upgrade-safety verdict for safe contract upgrades. - Build & ship — typed transaction envelope builder, simulate, native transaction signing, submit, identity/keystore management, multi-contract workspace topological deployments, and upgrade breaking-change guards.
Most commands are offline; only on-chain reads (inspect, storage, tx, events, account, fee, wasm metadata) need an RPC endpoint.
| Capability | Command |
|---|---|
Decode base64 XDR (ScVal, TransactionEnvelope, ContractEvent) |
sdkt decode |
| Inspect contract ABI + storage | sdkt inspect, sdkt storage check |
| Storage TTL / rent analysis | sdkt storage analyze, sdkt storage estimate |
| Transaction inspect / simulate / submit / build | sdkt tx * |
| Event explorer | sdkt events |
| Account balances + signers | sdkt account |
| Dynamic fee estimate | sdkt fee estimate |
| Multi-contract Orchestration | sdkt build, sdkt project deploy |
| WASM Operations | sdkt wasm inspect, sdkt wasm metadata, sdkt wasm cache, sdkt verify, sdkt health |
| Offline WASM diff + upgrade-safety verdict | sdkt diff --upgrade-safety |
| Static security audit | sdkt audit |
| ED25519 keystore | sdkt identity |
| Named network profiles | sdkt network |
| Project scaffolding | sdkt init |
Deploy (with --deny-breaking guard) |
sdkt deploy |
| Contract invocation (read-only) | sdkt call |
| Contract invocation (state-changing, end-to-end) | sdkt invoke |
Running sdkt takes under five minutes. Pick an install method, verify, then
run your first command.
Choose one of the following methods.
curl -fsSL https://raw.githubusercontent.com/SaboLabs/soroban-devkit/main/install.sh | bashThe script detects your OS/arch, downloads the matching release binary,
verifies its SHA-256 checksum, and installs sdkt to ~/.local/bin/sdkt.
-
Open the Releases page and download the latest release for your platform:
Platform Asset Linux (x86_64) sdkt-x86_64-unknown-linux-gnu.tar.gzLinux (aarch64) not in v2.5.0 Release — use install.shorcargo install sdkt-climacOS (Intel) sdkt-x86_64-apple-darwin.tar.gzmacOS (Apple Silicon) sdkt-aarch64-apple-darwin.tar.gzWindows x86_64 is not included in the v2.5.0 GitHub Release. Windows users can install via
cargo install sdkt-clior build from source. -
Extract and run:
Linux/macOS:
tar -xzf sdkt-x86_64-unknown-linux-gnu.tar.gz # Linux x86_64 chmod +x sdkt ./sdkt --version # Optional: make it available system-wide sudo mv sdkt /usr/local/bin/
Windows (v2.5.0): no GitHub Release zip yet. Use crates.io or source:
cargo install sdkt-cli sdkt --version
git clone https://github.com/SaboLabs/soroban-devkit
cd soroban-devkit
cargo install --path crates/sdkt-cli
sdkt --versionsdkt --version
# sdkt <version>
sdkt --help
# Commands:
# decode Decode base64-encoded XDR to JSON
# wasm Manage WASM metadata and caching
# diff Offline diff of two contract WASM files
# audit Static security analysis of a Soroban contract source file
# init Initialize a new Soroban contract project
# deploy Deploy a contract (Upload WASM + Instantiate)
# ... (run `sdkt --help` to see all)A successful sdkt --version means the binary is installed and on your PATH.
Generate completion scripts for your shell and source them:
# bash
sdkt completions bash > ~/.local/share/bash-completion/completions/sdkt
# zsh
sdkt completions zsh > "${fpath[1]}/_sdkt"
# fish
sdkt completions fish > ~/.config/fish/completions/sdkt.fish
# powershell
sdkt completions powershell > sdkt.ps1
# then in your profile: . ./sdkt.ps1Supported shells: bash, zsh, fish, powershell (also elvish).
No install: drop a .wasm on the
Web Playground
(ContractSpec / exports / hash; bytes never leave the browser).
CLI: inspect a compiled contract WASM that ships with the repo:
sdkt wasm inspect crates/sdkt-cli/tests/fixtures/us_old.wasmThen follow the guided walkthrough in docs/quick-start.md — it covers inspect, audit, and upgrade-safety diff step by step.
- Inspect Soroban WASM — read ABI, functions, events, and metadata from any
compiled contract, offline (
sdkt wasm inspect). - Compare contract upgrades safely — diff two WASM files and get a
breaking-change verdict before deploying (
sdkt diff --upgrade-safety). - Audit contracts offline — static security analysis of contract source with
no network or secrets (
sdkt audit). - Scaffold new projects — generate a ready-to-build Soroban contract
(
sdkt init). - Deploy with upgrade protection — upload and instantiate, aborting on a
non-backwards-compatible upgrade (
sdkt deploy --deny-breaking).
A typical first-contract workflow:
# 1. Create a new contract project
sdkt init my-contract --minimal
cd my-contract
# 2. Build the contract into a Soroban WASM
# Output: target/wasm32-unknown-unknown/release/<project>.wasm
sdkt build
# 3. Generate a local signing identity
sdkt identity generate my-deployer
# 4. Configure Testnet (one-time setup)
sdkt network add testnet \
--rpc-url https://soroban-testnet.stellar.org \
--passphrase "Test SDF Network ; September 2015" \
--friendbot https://friendbot.stellar.org
# 5. Fund your identity (Testnet only)
# Copy the public key from `sdkt identity show my-deployer` and paste it at:
# https://friendbot.stellar.org
# OR use the CLI directly:
sdkt identity fund my-deployer --network-profile testnet
# 6. Generate a 20-byte salt (40 hex characters)
openssl rand -hex 20
# 7. Deploy to Testnet
sdkt deploy \
--wasm target/wasm32-unknown-unknown/release/my_contract.wasm \
--salt <paste-hex-from-step-6> \
--identity my-deployer \
--network-profile testnet
# 8. Invoke a contract function (state-changing: sequence → simulate → sign → submit → poll)
sdkt invoke <CONTRACT_ID> increment --args u32:1 --identity my-deployer --network-profile testnet
mainFor a detailed explanation of each step, see Deploy a single contract.
Full options — including the wasm-plugins / plugins feature flags,
installing from crates.io, and updating — are in
docs/installation.md.
The sdkt audit static analysis engine supports third-party plugins.
wasm-plugins(Recommended): Build withcargo install --path crates/sdkt-cli --features wasm-pluginsto load platform-independent, sandboxed.wasmplugins.plugins: Build with--features pluginsto load native shared libraries (.so,.dylib).
See docs/plugin-authoring.md for how to build or use custom rules.
| Command | Purpose |
|---|---|
sdkt decode <xdr> |
Decode base64 XDR (`--type ScVal |
sdkt inspect <contract-id> |
Inspect a contract's ABI and storage (--abi <wasm> for ABI-aware decode). |
sdkt storage check <contract-id> |
Storage TTL / rent visibility (--abi <wasm>). |
sdkt storage analyze <contract-id> |
Classify Instance / Persistent / Temporary storage entries + TTL summary. |
sdkt storage estimate <wasm-path> |
Estimate storage cost for a WASM. |
sdkt storage read <contract-id> --key-xdr <BASE64_XDR> |
Read a contract storage entry by its complete LedgerKey. ABI optional for ScVal formatting. |
sdkt storage extend <contract-id> --ledgers <N> |
Extend TTL of known footprint keys (ExtendFootprintTtl). Instance key is always included; extra keys via --key. Does not restore archived entries. |
sdkt storage restore --contract <id> --envelope <xdr> |
Restore archived entries (RestoreFootprint) using the restorePreamble from simulating the failed invocation. --dry-run shows the keys and fee without submitting. |
sdkt tx inspect <hash> |
Transaction status / ledger inclusion. |
sdkt tx validate --envelope <xdr> |
Offline pre-flight validation of an envelope (parses + structural checks). |
sdkt tx decode <xdr> |
Human-readable view of an envelope (source, fee, operations with decoded args, footprint, signatures) for review before signing. Offline; --format json for a structured breakdown. |
sdkt tx simulate <xdr> |
Offline pre-flight via simulateTransaction (RPC). --abi <wasm> decodes the invoke result via the contract spec. |
sdkt tx sign --input <xdr> --identity <name> |
Sign an envelope with a local ED25519 identity — fully offline. |
sdkt tx submit <xdr> |
Submit a transaction (with optional poll; RPC). |
sdkt tx build |
Typed envelope builder. |
sdkt events <contract-id> |
Emitted-contract event explorer (--abi <wasm>; --topic <SYMBOL> filters by event name server-side). |
sdkt account <address> |
Account balances (native + trustlines) and complete typed signers (Horizon-enriched with RPC fallback). |
sdkt call <contract> <function> [--args TYPE:VALUE...] |
Read-only contract invocation. No signing, no submission. Returns result + events. --abi <wasm> decodes the result via the contract spec; --abi-contract <id> fetches the deployed contract's on-chain WASM instead. |
sdkt diff |
Offline comparison of WASM binaries and API surfaces. |
sdkt diff --old-wasm <A> --new-wasm <B> |
Offline ABI/function/event/type diff of two WASM files. Add --upgrade-safety for a breaking-change verdict. |
sdkt build |
Compile workspace rust contracts into optimized WASMs. |
sdkt deploy --wasm <file> [--salt <salt>] [--arg <type:value>...] |
Upload WASM + instantiate. Salt is auto-generated if omitted (see Deploy a single contract below). Pass constructor arguments via repeated --arg type:value flags (uses CreateContractV2). Add --deny-breaking --old-wasm <deployed.wasm> to abort on a non-backwards-compatible upgrade. |
sdkt project deploy |
Deploy multi-contract workspace orchestrating topological dependency sorting. |
sdkt verify --contract <ID> [--wasm <file>] [--network <net>] |
Verify a deployed contract matches a local WASM (offline hash vs on-chain hash). |
sdkt health --contract <ID> [--wasm <file>] [--network <net>] |
Unified read-only contract posture report (WASM, storage, TTL, health verdict). |
sdkt wasm inspect <file> |
Inspect offline WASM metadata, sections, and specifications. |
sdkt wasm metadata --contract <contract> |
WASM metadata for a deployed contract (cached). |
sdkt wasm cache |
Manage the WASM cache (info / remove / clear). |
sdkt audit <path.rs> |
|
sdkt identity <generate|import|list|show|delete|default> |
ED25519 keystore management. |
sdkt identity fund <name> --network-profile <NAME> |
Fund an identity via Stellar Testnet Friendbot. |
sdkt network <add|list|show|remove> |
Named network profiles (RPC URL + passphrase). Combine with --network-profile <NAME> on any RPC command to avoid repeating endpoints; --rpc-url / --network-passphrase override. |
sdkt init <name> |
|
sdkt lock generate |
Write sdkt.lock recording each built artifact's SHA-256 + deploy order (after sdkt build). |
sdkt lock verify |
Verify sdkt.lock against current on-disk artifacts and package dependencies (lock matches manifest, git commits, path existence). Advisory; never fails the build. Prints ✓ lock file verified / ✓ package dependencies verified or lists drift. |
sdkt lock show |
Print the current sdkt.lock contents. |
sdkt package validate |
Validate the local package manifest ([package] metadata + local/git [dependencies] graph). Offline; never touches the network or a registry. |
sdkt package fetch [--force] |
Fetch declared dependencies into .sdkt-cache (local path passthrough; git clone/checkout). Never builds. --force updates existing checkouts. |
sdkt package update [--check] [--dry-run] [--format pretty|json] |
Synchronize dependencies: refresh git deps to the latest available commit, rewrite sdkt.lock. rev stays pinned; tag/branch update on drift. --check reports only; --dry-run previews without touching cache/lock. |
sdkt deploy uploads a Soroban WASM to the network and instantiates it as a
new contract instance. It is the single-contract counterpart to
sdkt project deploy (which orchestrates multi-contract workspaces).
A fresh deploy requires a built WASM, a funded signing identity, and a salt. Follow these steps in order:
# 1. Build your contract into a Soroban WASM
# (from a project created with `sdkt init <name>`)
sdkt build
# 2. Generate a local ED25519 identity (used to sign the deploy transactions)
sdkt identity generate my-deployer
# 3. Configure the Testnet network profile
sdkt network add testnet \
--rpc-url https://soroban-testnet.stellar.org \
--passphrase "Test SDF Network ; September 2015" \
--friendbot https://friendbot.stellar.org
# 4. Fund your identity on Testnet
# Copy the public key from `sdkt identity show my-deployer` and paste it at:
# https://friendbot.stellar.org
# (Friendbot is a Testnet faucet — it does NOT work on Mainnet.)
# 5. Deploy (salt is auto-generated if omitted)
sdkt deploy \
--wasm target/wasm32-unknown-unknown/release/<project>.wasm \
--identity my-deployer \
--network-profile testnet
# Or with explicit salt for deterministic address:
# openssl rand -hex 20
# sdkt deploy --wasm ... --salt <40-hex-chars> --identity my-deployer --network-profile testnetReplace <project> with your crate name (the name field in Cargo.toml).
The --wasm path follows the Cargo convention
target/<profile>/<crate>.wasm — adjust if your build profile or target
directory differs.
--wasm(required): Path to a compiled Soroban WASM (32kb+ afterstellar contract build).--salt(optional): 40-character hex string (20 bytes). Auto-generated if omitted. It salts the contract ID derivation so the same deployer + WASM + salt always yields the same contract address across networks. Provide explicitly for deterministic/reproducible deployment.--identity <name>: ED25519 identity fromsdkt identityused to sign both transactions (upload + instantiate). Defaults todefault.--arg <type:value>: Constructor argument intype:valueformat (e.g.--arg u32:42,--arg string:hello,--arg bool:true,--arg bytes:0a0b,--arg address:G...,--arg symbol:init). Pre-encoded base64 ScVal strings are also accepted as-is. Can be repeated for multiple constructor arguments. Contracts deployed with arguments useCreateContractV2.--format <pretty|json>: Output format (defaultpretty).--deny-breaking: Abort if the new WASM is not backwards-compatible with the currently deployed contract. Requires--old-wasm.--old-wasm <file>: Path to the existing on-chain baseline WASM. Used only with--deny-breaking.
- Resolve the network (flag → profile →
.sdkt.toml→ built-in Testnet default). - Apply the mainnet-safety guard (mainnet requires explicit network selection).
- Optionally run the
--deny-breakingupgrade-safety check. - Load the signing identity and its key.
- Upload — build an
UploadContractWasmtransaction, simulate, apply the returnedSorobanTransactionDataand authorization entries, sign, submit, and poll for confirmation. - Instantiate — build a
CreateContract(orCreateContractV2when constructor arguments are provided via--arg) transaction with the WASM hash from the upload, the user-supplied salt, and any constructor arguments, simulate, finalize, sign, submit, and poll for confirmation. - Derive the contract ID from
Hash(networkId || HashIdPreimage::ContractId{...})and report the deployment result.
- Mainnet deployments are refused unless the operator explicitly selects the
network (via
--rpc-url+--network-passphrase, or--network-profilewhose passphrase is mainnet). Implicit Testnet defaults cannot touch mainnet. - The salt must be exactly 40 hex characters; any other length or non-hex input is rejected before any network call.
Deployment Result:
WASM Hash: e71250b5...
Contract ID: 0bde4658...
Upload TX: c8599bb1...
Create TX: 1e934b4e...
Status: SUCCESS
A .sdkt.toml workspace declares contracts under [contracts.<alias>]. Each
contract may declare dependencies using either the canonical depends_on
or the legacy deploy_after field — both are merged during
resolution:
[contracts.token]
path = "contracts/token"
[contracts.router]
path = "contracts/router"
depends_on = ["token"]sdkt build, sdkt project deploy, and sdkt lock generate all resolve the
same dependency graph via a single topological sort (Kahn's algorithm), so the
deploy order is deterministic and identical across commands. Invalid graphs are
rejected up front with a clear error:
- Unknown dependency — a
depends_on/deploy_afterentry that is not a defined[contracts.*]. - Self-dependency — a contract that lists itself as a dependency.
- Duplicate dependency — the same dependency declared more than once
(e.g. in both
depends_onanddeploy_after). - Circular dependency — a cycle such as
a → b → a. - Duplicate contract name — two
[contracts.<alias>]tables (TOML parse error, surfaced instead of silently defaulting to an empty config).
sdkt package validate lays the groundwork for a future package registry
without introducing any network or remote-registry functionality. It checks
a local package manifest declared in .sdkt.toml:
[package]
name = "my-token"
version = "0.1.0"
description = "Example Soroban token"
[dependencies.math]
path = "../math"
[dependencies.auth]
path = "./auth"Validation rules (all offline — never performs network or registry I/O):
[package]must have anameand aversion.versionmust be a validMAJOR.MINOR.PATCHshape (with optional pre-release/build metadata), matching the common semver form.- Each
[dependencies.<name>]entry is exactly one source: a localpathor agitURL (never both).deny_unknown_fieldsrejects unrecognized keys so future source kinds (e.g. a registry) are explicit additions. - For
gitdeps, exactly one oftag/branch/revmust be set, and the URL must usehttps/http/git/ssh(or thegit@host:org/repoSCP form). - No self-dependency (a dependency key equal to the package
name). - For
pathdeps, the referenced directory must exist. - The dependency graph is checked for cycles (reusing the same topological-sort core as contract deploy-order resolution).
sdkt package validate exits non-zero on the first failure and prints a clear
message; with --format json it emits {"valid": true} or
{"valid": false, "error": "..."}.
sdkt package fetch materializes declared dependencies into a deterministic
local cache at .sdkt-cache (no registry, no authentication helpers, never
builds automatically):
[dependencies]
math = { path = "../math" }
token = { git = "https://github.com/org/token", tag = "v1.2.0" }
access = { git = "https://github.com/org/access", rev = "<commit>" }
utils = { git = "https://github.com/org/utils", branch = "main" }pathdeps are passed through (validated already byvalidate).gitdeps are cloned/checked out via the systemgitCLI into.sdkt-cache/git/<stable-key>/, then pinned to the requestedtag/branch/rev. Re-running reuses an existing checkout unless--forceis passed. A future registry source plugs into the sameDependencyFetcherabstraction without touching callers.
The lock file (sdkt.lock) records each dependency's source, git URL,
requested reference, and resolved commit SHA (when available), so fetches are
reproducible. Local path deps remain unchanged in the lock.
Save an RPC endpoint once and reference it from any RPC command instead of repeating full URLs:
# Save a profile
sdkt network add testnet \
--rpc-url https://soroban-testnet.stellar.org \
--passphrase "Test SDF Network ; September 2015"
# Use it (any RPC command)
sdkt inspect <CONTRACT_ID> --network-profile testnet
sdkt account <ADDRESS> --network-profile testnetEvery RPC command (inspect, verify, health, storage, events, account,
tx, fee, wasm, deploy, project deploy) also accepts explicit
--rpc-url <URL> and --network-passphrase <PASSPHRASE> override flags.
Precedence (highest wins): explicit --rpc-url / --network-passphrase >
--network-profile > .sdkt.toml [network] > built-in testnet default.
Commands without these flags behave exactly as before. (tx sign is offline and
excluded.) See docs/cli.md and docs/examples.md.
Most commands accept --format json for scripting / CI integration.
- Audit every PR — gate merges on
sdkt audit(fails oncritical). See docs/ci-cd.md. - Safe upgrades — run
sdkt diff --upgrade-safetyin release CI to block breaking contract changes. - Local analysis —
decode,diff, andauditneed no RPC; run them in CI or locally without secrets.
sdkt can build, validate, simulate, sign, and submit a Soroban
transaction entirely from the CLI. Signing is fully offline — it uses a
local ED25519 identity from the keystore (sdkt identity); no RPC, no secret
ever leaves your machine.
# 1. Create a local signing identity (offline)
sdkt identity generate alice
# 2. Build an unsigned envelope (offline)
sdkt tx build \
--source <SOURCE_ACCOUNT> \
--sequence <SEQ> \
--contract <CONTRACT_ID> \
--function hello \
--output unsigned.xdr
# 3. Validate the envelope offline and review what it does
sdkt tx validate --envelope unsigned.xdr
sdkt tx decode unsigned.xdr
# 4. Simulate against the network (RPC) to catch failures early
sdkt tx simulate --envelope unsigned.xdr
# 5. Sign with the local identity (offline) — picks testnet by default
sdkt tx sign --input unsigned.xdr --output signed.xdr --identity alice --network testnet
# 6. Submit the signed envelope (RPC)
sdkt tx submit --envelope signed.xdrNotes:
tx signtakes--network testnet|mainnet|futurenet|custom:<passphrase>; the network only affects the signature hash, so signing needs no RPC.tx submit/tx simulateread the network from.sdkt.toml([network]section) or fall back to the default testnet RPC.--outputis optional; without it, the signed base64 envelope is printed to stdout. Use--format jsonfor scripting.
Copy-paste recipes for every subcommand are in docs/examples.md.
sdkt ships a reusable GitHub composite Action. See
docs/ci-cd.md for copy-paste workflows (audit-on-PR,
upgrade-safety-on-release).
- docs/quick-start.md — five-minute first-time walkthrough (offline).
- docs/testnet-walkthrough.md — end-to-end Testnet loop: identity → fund → deploy → invoke → events / storage.
- docs/getting-started.md — deeper offline
diffandauditexamples. - docs/examples.md — command recipes & CI gating.
- docs/installation.md — build / install / features.
- docs/compatibility.md — real-world contract compatibility matrix.
- docs/ci-cd.md — CI/CD with the reusable Action.
- docs/adoption.md — ecosystem adoption and integration evidence.
- SECURITY.md — supported versions and vulnerability reporting.
- CONTRIBUTING.md — how to contribute.
Additional references: docs/cli.md (full command reference), docs/faq.md (FAQ), docs/plugin-authoring.md (write your own audit rules), and ROADMAP.md · CHANGELOG.md.
See CONTRIBUTING.md. All contributions are welcome — docs, tests, and small fixes are great first PRs. Please follow the Code of Conduct.
MIT.