> For the complete documentation index, see [llms.txt](https://tollcraft.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tollcraft.gitbook.io/docs/concepts/cost_rationale.md).

# Cost Rationale

> This page explains *what* Soroban charges for and *which operations dominate* a contract's budget. It is the shared reference behind every lint in this repository. Written for contract developers, not compiler engineers.

***

## Budgets and Limits

Every Soroban transaction runs against a **per-transaction resource budget**. If execution exhausts the budget, the transaction fails — no partial state is committed. The budget is defined by network-wide limits (set by validator consensus) and reported in two dimensions[^1]:

| Resource dimension                  | Limit type | Charged in fee?   |
| ----------------------------------- | ---------- | ----------------- |
| **CPU instructions**                | Hard cap   | Yes               |
| **Memory (RAM)**                    | Hard cap   | No (but enforced) |
| **Ledger entry reads**              | Hard cap   | Yes               |
| **Ledger entry writes**             | Hard cap   | Yes               |
| **Ledger I/O (bytes read/written)** | Hard cap   | Yes               |
| **Transaction size (bytes)**        | Hard cap   | Yes               |

{% hint style="info" %}
Current per-transaction limits are published on the [Stellar Lab](https://lab.stellar.org/) under *Resource Limits & Fees*. These values change only through validator consensus.
{% endhint %}

***

## Metered Resources

Soroban's fee model is **multidimensional**: the resource fee is the sum of charges for several independent resource types[^1].

### 1. CPU Instructions

Every Wasm instruction the guest executes and every host function the contract calls is metered as CPU instructions. The host environment tracks more than 85 distinct **cost types** (`ContractCostType`), each with its own calibrated cost model of the form `y = a + bx`, where `x` is a runtime input size.

Key cost types that matter for linting:

| Cost type                        | What it models                                           |
| -------------------------------- | -------------------------------------------------------- |
| `WasmInsnExec`                   | Baseline cost of one Wasm instruction                    |
| `DispatchHostFunction`           | Overhead of crossing from Wasm into the host environment |
| `MemAlloc` / `MemCpy` / `MemCmp` | Memory-management operations                             |
| `VisitObject`                    | Accessing a host object from storage                     |

**The per-instruction cost is not uniform.** A `ComputeSha256Hash` call costs orders of magnitude more CPU than a `WasmInsnExec` — but every cost type is ultimately summed into the same instruction counter[^2].

### 2. Memory (RAM)

Memory is metered in bytes but **not included in the resource fee**. It is still subject to a hard per-transaction cap. If a contract allocates beyond the limit, execution is terminated.

For linting purposes, memory is a secondary concern: storage and CPU dominate the fee.

### 3. Storage: Ledger Entry Accesses and Ledger I/O

Storage operations are the **single most expensive resource** a typical Soroban contract can consume[^3]. The fee model charges in two sub-dimensions[^1]:

* **Ledger entry accesses** — each distinct storage key read or written counts as one access, regardless of its size.
* **Ledger I/O** — the total number of bytes read from or written to the ledger.

A single `env.storage().instance().set(&key, &val)` therefore incurs: one write access + `size_of(val)` write bytes + any CPU instructions for serialization. Repeating this inside a loop multiplies every dimension.

### 4. Transaction Size & Bandwidth

The size of the submitted transaction envelope (in bytes) is charged for network propagation and historical storage. This is rarely a linting concern — most structural anti-patterns exhaust the CPU or storage budget long before the bandwidth cap.

### 5. Events and Return Values

Events emitted by the contract and the top-level return value are included in transaction metadata and contribute to the resource fee. These costs are refundable: the network charges the declared maximum up front and refunds the unused portion after execution[^1].

### 6. Ledger Space Rent

Every ledger entry a contract creates or extends has a Time-To-Live (TTL). Extending TTL or increasing entry size incurs a **rent payment**, priced dynamically based on ledger size. This is a refundable fee component and depends on the state of the network, not just the contract's code structure.

***

## What Dominates

For the patterns this linter catches, the resource hierarchy is:

| Rank                | Operation                          | Primary resource consumed       |
| ------------------- | ---------------------------------- | ------------------------------- |
| 1 (most expensive)  | **Storage writes**                 | Ledger entry writes + I/O bytes |
| 2                   | **Storage reads**                  | Ledger entry reads + I/O bytes  |
| 3                   | **Host function calls**            | CPU (dispatch + function work)  |
| 4                   | **Wasm arithmetic / control flow** | CPU (WasmInsnExec)              |
| 5 (least expensive) | **Memory operations**              | RAM (capped, not charged)       |

**Storage writes dominate** because they consume four resources simultaneously: a ledger entry access, write I/O bytes, the serialization CPU cost, and (for new entries) space rent. A single storage write in a loop can cost more than the rest of the loop body combined.

**Host function calls** (e.g., `env.ledger().sequence()`) are cheaper than storage but still expensive relative to pure Wasm because they pay the `DispatchHostFunction` overhead plus whatever work the host performs[^2]. Calling a constant-returning host function inside a loop is pure waste.

***

## The Local-vs-Network Gap

One of the most surprising results from empirical measurement is how much local estimates can differ from real network costs. Measured on the example contract from the sibling repository `soroban-budget-assert`:

| Execution mode                             | CPU instructions | Gap vs. testnet                 |
| ------------------------------------------ | ---------------- | ------------------------------- |
| Raw Rust (native test)                     | 143,887          | **Under**estimates by \~**81%** |
| Local WASM (`register_contract_wasm`)      | 901,816          | **Over**estimates by \~**19%**  |
| Testnet simulation (`simulateTransaction`) | 756,678          | Ground truth                    |

{% hint style="warning" %}
Raw Rust test estimates are **unreliable** for budget decisions — they can miss real network cost by over 80%. Even WASM-mode local estimates can be off by double-digit percentages, and the direction (over vs. under) depends on the build profile.
{% endhint %}

**What this means for linting:** The linter catches *structural* anti-patterns that are expensive regardless of the local/network gap. A storage write in a loop is expensive everywhere. But the *magnitude* of savings from fixing it can only be known by running the compiled WASM against a network simulation — which is the purpose of the sibling project `soroban-budget-assert`.

***

## Per-Lint Resource Summary

Each lint in this repository targets a specific resource dimension:

| Lint                                                                                                                                        | Primary resource targeted                             | Why it matters                                                                                                                                                                  |
| ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`soroban_storage_in_loop`](/docs/lints/lints/soroban_storage_in_loop.md)                                                                   | **Storage** (ledger entry accesses + I/O bytes)       | Storage writes are the #1 cost driver; multiplying them by loop count is the most expensive pattern this tool detects.                                                          |
| [`unnecessary_host_function_call`](/docs/lints/lints/unnecessary_host_function_call.md)                                                     | **CPU** (host function dispatch)                      | Host calls are expensive relative to pure Wasm; repeating a constant-result call inside a loop wastes CPU.                                                                      |
| [`signature_verification_in_loop`](https://github.com/Tollcraft/soroban-cost-linter/tree/main/docs/lints/signature_verification_in_loop.md) | **CPU** (elliptic-curve cryptographic host functions) | Signature verification is one of the most expensive host functions available; verifying one at a time in a loop is a sign that a batch/aggregate scheme should be used instead. |
| [`redundant_env_clone`](/docs/lints/lints/redundant_env_clone.md)                                                                           | **CPU** (memory + dispatch overhead)                  | Cloning `Env` triggers `MemAlloc`/`MemCpy` and unnecessary object visits; the clone is never needed.                                                                            |
| [`excessive_vec_capacity`](https://github.com/Tollcraft/soroban-cost-linter/tree/main/docs/lints/excessive_vec_capacity.md)                 | **Memory** (guest linear memory, hard-capped)         | A large hard-coded capacity is charged against the guest's memory cap the moment it's allocated, whether or not it's ever filled.                                               |

***

## What We Don't Yet Know

* **Exact per-instruction CPU costs for every `ContractCostType`** — the calibrated model parameters (`a`, `b` for each cost type) are set by network consensus and are not published in developer-facing documentation. They can be inspected in the `rs-soroban-env` source repository[^4].
* **Decomposed storage costs** — the ratio of "ledger entry access fee" to "I/O byte fee" is not specified independently. The total storage fee is what matters for linting, but measuring the split requires network simulation.

{% hint style="info" %}
Local measurements are available in the [`cost_benchmarks`](https://github.com/Tollcraft/soroban-cost-linter/tree/main/cost_benchmarks/README.md) crate. Run `cargo test -- --nocapture` to see before/after budget deltas for each lint pattern on `Env::default()`. These numbers are **directional** (they show relative savings) but are subject to the [Local-vs-Network Gap](#the-local-vs-network-gap) described above.
{% endhint %}

***

## References

[^1]: Stellar Development Foundation, *"Understanding Fees, Resource Limits, and Metering"*, Stellar Docs. <https://developers.stellar.org/docs/learn/fundamentals/fees-resource-limits-metering>

[^2]: Stellar XDR Specification, *`ContractCostType` enum* (86 variants). <https://docs.rs/stellar-xdr/latest/stellar_xdr/enum.ContractCostType.html>

[^3]: `soroban-cost-linter` README — *"Storage operations in Soroban are the most expensive resource."* <https://github.com/Tollcraft/soroban-cost-linter>

[^4]: Stellar `rs-soroban-env` — host-side cost model definitions. <https://github.com/stellar/rs-soroban-env>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://tollcraft.gitbook.io/docs/concepts/cost_rationale.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
