Audience:
soroban developer·getting started·writing launch.jsonTL;DR: A
sorobanlaunch configuration describes an ordered sequence of transactions run against one fresh local ledger, and names which transaction to trace and debug. You set up whatever state your call depends on (deploy other contracts, run a constructor, seed storage) as earlier transactions, then pointtraceat the one you want to step through. This document covers every field, the argument encodings (including enums, tuples, structs, and addresses), the${…}substitution tokens, and offline replay — with worked examples. For the editor tour see../examples/README.md; for the CLIs seetrace-cli.mdanddap-cli.md.
A launch configuration runs in one of two modes:
- Live — the default. You give a
transactionsarray; the debugger spawns (or attaches to) a local komet-node, runs the whole sequence against one fresh ledger, and drops you into a debug session on the transaction named bytrace. - Replay — you give a
rawTracefile. The debugger replays a previously recorded execution with no network and no toolchain. Optionally pair it with awasmPathfor Rust source mapping. See Replay mode.
A configuration is one JSON object in your .vscode/launch.json under
configurations. Every live configuration shares this skeleton:
transactions is an ordered, non-empty array of steps. Each step is either
a deploy (upload a contract and register a handle) or an invoke (call a
function on a deployed handle). Steps run in order against one accumulating
ledger, so state written by one step is visible to later steps.
"transactions": [
{ "kind": "deploy", "id": "pool", "contract": "${workspaceFolder}" },
{ "kind": "invoke", "contract": "pool", "function": "__constructor",
"args": { "admin": "${sourceAddress}" } },
{ "kind": "invoke", "contract": "pool", "function": "supply",
"args": { "requests": [[{ "tag": "Native" }, "1000"]] } }
]Notes on semantics that shape how you author a sequence:
- A constructor is just an invoke. komet runs a function literally named
__constructoras an ordinary call, and its storage writes persist — so express contract initialization as an explicitinvokestep, not as part of the deploy. - Every transaction gets a distinct hash. Two byte-identical invokes would otherwise be deduplicated by the node; the runner assigns an incrementing account sequence per submission so repeated identical calls each execute.
- A reverting transaction stays debuggable. The sequence never stops on a failed transaction; the traced step's trace is fetched regardless of status.
| Field | Required | Description |
|---|---|---|
kind |
✅ | "deploy". |
id |
✅ | Handle name. Later invoke steps reference it via their contract field, and trace can select this deploy's transaction by this id. Must be unique. |
contract |
one of contract/wasm |
Path to a contract crate directory (containing Cargo.toml) to build. |
wasm |
one of contract/wasm |
Path to a prebuilt .wasm. Overrides building from contract. |
buildCommand |
Command used to build a contract directory. Defaults to stellar contract build (or the soroban.stellar.path setting). Ignored when wasm is given. |
|
debugInfo |
Build with DWARF debug info for Rust source mapping (default true). Set false to debug at the wasm level only. Ignored when wasm is given. |
| Field | Required | Description |
|---|---|---|
kind |
✅ | "invoke". |
contract |
✅ | Handle id of an earlier deploy step. |
function |
✅ | Name of the contract function to call. |
args |
Arguments to the function (see Arguments). | |
id |
Optional label so trace can select this invoke's transaction. |
trace names which transaction feeds the debug session:
| Value | Meaning |
|---|---|
"last" (default) |
The final step in transactions. |
| a number | A 0-based index into transactions. |
| a string | A step id — a deploy's id or an invoke's optional id. |
An invoke step's args is an object keyed by the function's parameter
names. Values follow the contract's own spec, so composite types work
naturally:
| Rust type | JSON value |
|---|---|
u32 / i32 / bool |
1, -2, true |
u64/i64/u128/i128/u256/i256 |
a decimal string, e.g. "1000" (avoids JS precision loss) |
Symbol / String |
"hello" |
Address |
a G… (account) or C… (contract) string |
| a unit enum variant | { "tag": "Native" } |
| an enum variant with data | { "tag": "Other", "values": [7] } |
a tuple or Vec |
a JSON array, e.g. [a, b] |
| a struct | an object keyed by field name |
Bytes |
a hex string |
Composites nest, so a Vec<(Asset, i128)> where Asset is an enum is:
"args": { "requests": [ [ { "tag": "Native" }, "1000" ],
[ { "tag": "Other", "values": ["C…"] }, "-50" ] ] }args also accepts the lower-level positional form: an array of
{ "type", "value" } objects, one per parameter, encoded without consulting the
spec. Prefer the named object form above; the positional form is a fallback for
when you want explicit control over each ScVal's type:
"args": [ { "type": "u32", "value": 1 }, { "type": "u32", "value": 2 } ]Two tokens are expanded inside string args values (and inside nested
composites):
| Token | Expands to |
|---|---|
${sourceAddress} |
The source account's address (the account that signs every transaction). |
${contract:<id>} |
The deployed contract address behind the handle <id>. Use it to wire one deployed contract's address into another's call. |
Alongside these, the standard VS Code variables such as ${workspaceFolder}
work in path fields like contract and wasm.
| Field | Description |
|---|---|
transactions |
The ordered live sequence (required for live mode). |
trace |
Which transaction to debug (see trace). |
sourceSecret |
Source account secret (S…) used to sign every transaction. A deterministic account is derived and self-seeded if omitted; its address is available as ${sourceAddress}. |
node |
Local-network connection/spawn settings: attach, host, port, command, ioDir. |
rawTrace |
Replay mode: path to a recorded JSONL trace to replay instead of running a live sequence. |
wasmPath |
Replay mode only: a .wasm supplying disassembly and DWARF source mapping for the replayed trace. |
Two VS Code settings let you point at executables that aren't on your PATH:
soroban.stellar.path and soroban.kometNode.path.
Replay needs no toolchain and no node — it re-reads a trace you (or a teammate)
recorded earlier, which makes it ideal for reproducible bug reports. Give a
rawTrace path; add a wasmPath to get Rust source mapping instead of the
wasm-level fallback:
{
"type": "soroban",
"request": "launch",
"name": "Replay add(4, 3)",
"rawTrace": "${workspaceFolder}/traces/add.trace.jsonl",
"wasmPath": "${workspaceFolder}/../test/fixtures/adder-debug.wasm"
}Record a trace with the soroban-trace CLI.
The smallest live configuration: build the crate in the workspace folder, invoke
one function, and debug it. There is one deploy and one invoke, and trace
defaults to the last step.
{
"type": "soroban",
"request": "launch",
"name": "Debug add(1, 2)",
"transactions": [
{ "kind": "deploy", "id": "adder", "contract": "${workspaceFolder}" },
{ "kind": "invoke", "contract": "adder", "function": "add",
"args": { "a": 1, "b": 2 } }
]
}Deploy several contracts, initialize them, wire their addresses together with
${contract:<id>}, seed some state, then debug a later call. Here we trace the
swap invoke by giving it an id and pointing trace at it — so the earlier
setup runs but the session opens on the call you care about.
{
"type": "soroban",
"request": "launch",
"name": "Debug router.swap",
"transactions": [
{ "kind": "deploy", "id": "token_a", "wasm": "${workspaceFolder}/wasm/token.wasm" },
{ "kind": "deploy", "id": "token_b", "wasm": "${workspaceFolder}/wasm/token.wasm" },
{ "kind": "deploy", "id": "router", "contract": "${workspaceFolder}/router" },
{ "kind": "invoke", "contract": "token_a", "function": "__constructor",
"args": { "admin": "${sourceAddress}", "decimals": 7, "name": "A", "symbol": "A" } },
{ "kind": "invoke", "contract": "token_b", "function": "__constructor",
"args": { "admin": "${sourceAddress}", "decimals": 7, "name": "B", "symbol": "B" } },
{ "kind": "invoke", "contract": "router", "function": "__constructor",
"args": { "token_a": "${contract:token_a}", "token_b": "${contract:token_b}" } },
{ "kind": "invoke", "contract": "token_a", "function": "mint",
"args": { "to": "${sourceAddress}", "amount": "1000000" } },
{ "kind": "invoke", "id": "the_swap", "contract": "router", "function": "swap",
"args": { "from": "${sourceAddress}", "amount_in": "1000", "min_out": "990" } }
],
"trace": "the_swap"
}Enums, tuples, structs, nested vectors, addresses, and large integers, all in
named args:
{
"type": "soroban",
"request": "launch",
"name": "Debug pool.submit",
"transactions": [
{ "kind": "deploy", "id": "pool", "contract": "${workspaceFolder}" },
{ "kind": "invoke", "contract": "pool", "function": "submit",
"args": {
"from": "${sourceAddress}",
"spender": "${sourceAddress}",
"requests": [
[ { "tag": "Supply" }, "${contract:pool}", "1000000" ],
[ { "tag": "Borrow" }, "${contract:pool}", "-500" ]
],
"config": { "collateral": true, "cap": "170141183460469231731687303715884105727" }
} }
],
"trace": "last"
}
{ "type": "soroban", "request": "launch", "name": "…", // shown in the Run and Debug dropdown "transactions": [ … ], // the ordered sequence (required) "trace": "last" // which transaction to debug (optional, default "last") }