Soroban charges for what a transaction actually does: CPU instructions burned,
linear memory touched, and the ledger entries read and written. The network's
simulateTransaction RPC is the only place those numbers come from — it runs
the invocation against a real ledger snapshot and reports a minimum resource
fee the transaction must carry to be accepted.
StarForge surfaces those numbers in three places:
| Command | What it does |
|---|---|
starforge simulate resources |
Report CPU, memory, footprint, and minimum resource fee, and derive a submittable fee |
starforge cost resources |
The same report, then check the resulting fee against configured budgets |
starforge deploy --simulate / --dry-run |
Print the resource report inline before the deploy is confirmed |
Two input modes. Exactly one is required.
Offline — read a simulateTransaction response captured earlier (for
example by a CI job, or with curl):
starforge simulate resources --file simulation.jsonLive — simulate against a Soroban RPC endpoint:
starforge simulate resources \
--contract CCPYZ... \
--function transfer \
--arg GABC... --arg 1000 \
--network testnet| Flag | Default | Purpose |
|---|---|---|
--file <PATH> |
— | Saved simulateTransaction JSON response (conflicts with --contract) |
--contract <ID> |
— | Contract to simulate live |
--function <NAME> |
— | Function to simulate (required with --contract) |
--arg <VALUE> |
— | Function argument; repeat for multiple |
--arg-type <TYPE> |
inferred | Type for the matching --arg; must be supplied for all or none |
--network <NAME> |
testnet |
Network for live simulation |
--margin <PERCENT> |
20 |
Safety margin over the minimum resource fee (0–1000) |
--inclusion-fee <STROOPS> |
100 |
Per-operation inclusion (base) fee |
--json |
off | Emit the report as machine-readable JSON |
Example output:
Simulated Transaction Resources
─────────────────────────────────
CPU instructions 1,274,180
Memory (bytes) 1,275,072
Footprint entries 3 total (2 read-only, 1 read-write)
Ledger read bytes 8,192
Ledger write bytes 1,024
Simulated at ledger 1234567
─────────────────────────────────
Min resource fee 58,181 stroops (0.0058181 XLM)
Safety margin (20%) 11,636 stroops
Inclusion fee 100 stroops
Recommended fee 69,917 stroops (0.0069917 XLM)
Ledger state moves between simulation and submission. Submitting exactly
minResourceFee is a coin flip — a rent bump or a competing write in the same
ledger pushes the real cost above the simulated one and the transaction fails
with txINSUFFICIENT_FEE. The default 20% matches the Stellar CLI. Set
--margin 0 only when you are replaying against a frozen ledger.
Prices a saved simulation and checks it against the budgets configured with
starforge cost budget set:
starforge cost resources --file simulation.json --network mainnet --enforce| Flag | Default | Purpose |
|---|---|---|
--file <PATH> |
required | Saved simulateTransaction JSON response |
--network <NAME> |
testnet |
Network whose budgets to check against |
--margin <PERCENT> |
20 |
Safety margin over the minimum resource fee |
--inclusion-fee <STROOPS> |
100 |
Per-operation inclusion fee |
--enforce |
off | Exit non-zero if the fee would exceed a budget |
With --enforce this is a CI gate: the command exits non-zero when the
projected period spend crosses the configured limit, so a pipeline can refuse
to deploy without a human decision.
--simulate (and --dry-run, which implies it) now prints the resource
accounting alongside the fee:
Minimum Resource Fee 58181 stroops
CPU instructions 1274180
Memory (bytes) 1275072
Footprint 2 read-only, 1 read-write, 8192 B read, 1024 B written
Recommended fee 69917 stroops (0.0069917 XLM, includes a 20% margin)
If the RPC server returns no resource accounting, the extra lines are omitted rather than filled with invented numbers, and the reason is reported as a simulation warning.
| RPC field | Protocol 20 | Protocol 21 / 22 | Behaviour |
|---|---|---|---|
minResourceFee |
yes | yes | Required. Without it the response is rejected. |
cost.cpuInsns |
yes | deprecated | Falls back to the instruction count in transactionData. |
cost.memBytes |
yes | deprecated | Reported as not reported with a warning. |
transactionData |
yes | yes | Source of the footprint. Absent → footprint omitted, fee still reported. |
restorePreamble |
no | yes | Its minResourceFee is added to the plan and a restore warning is printed. |
Numeric fields are accepted as JSON numbers or JSON strings, because stellar-rpc serialises 64-bit counters as strings. Values that are negative, fractional, or non-numeric are rejected rather than coerced to zero.
- Not a Soroban RPC server — a response with neither
minResourceFeenortransactionDatais rejected with a message saying so, instead of reporting a fabricated fee. - Host failure (contract panic, bad auth, budget exceeded) — the
errorfrom the response is surfaced and no fee is planned. - Transport failure — a JSON-RPC
errormember is reported as an RPC error.
SimulationResult::fee previously reported cost.cpuInsns, an instruction
count, not a fee. Any script parsing that field as stroops was reading the
wrong number by roughly an order of magnitude. It now reports the RPC's
minResourceFee, falling back to 100000 stroops only when the server reports
no resource accounting at all.
The struct gained an optional resources field. It is #[serde(default)], so
previously serialised SimulationResult JSON still deserialises.
- Simulation responses are read from disk and from the network, so
--fileinputs are capped at 8 MiB before the JSON parser runs. transactionDatais decoded with an explicit XDR depth limit, so a nested payload cannot drive unbounded recursion.- Fee arithmetic is overflow-checked: a hostile or corrupted
minResourceFeeproduces an error rather than a wrapped total that looks affordable.
- GAS_OPTIMIZATION_GUIDE.md — reducing the resources in the first place
- docs/COMMAND_REFERENCE.md — every command and flag