Docs Técnicas
Simulation (Dry-Run)
The simulation endpoint lets you execute a contract entrypoint without committing any effects to the ledger. Wallets, dashboards, and audit tools use it to preview return values, accounting effects, emitted events, and execution cost before the user signs and submits the real transaction.
The simulation endpoint lets you execute a contract entrypoint without committing any effects to the ledger. Wallets, dashboards, and audit tools use it to preview return values, accounting effects, emitted events, and execution cost before the user signs and submits the real transaction.
POST /api/contracts/:id/simulate (no nonce, no signature, no state change)
→ executes entrypoint on live contract storage
→ returns effects + returns + cost
(nothing is written to state, no ledger entry is created)Endpoint
`POST /api/contracts/:contract_id/simulate`
Request body:
{
"entrypoint": "transfer",
"asset": "wallet:mint/ATLAS",
"args": [
{ "Address": "wallet:bob" },
{ "Integer": "500" }
],
"caller": "wallet:alice",
"block_height": 0,
"block_timestamp": 1700000000
}| Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | entrypoint | string | yes | Name of the @tx or @view function to call | | asset | string | no | Asset context (defaults to wallet:mint/ATLAS) | | args | array | no | Arguments in ContractArg format (default: []) | | caller | string | no | Address passed as ctx.caller (default: "simulate:caller") | | block_height | u64 | no | Block height for ctx.block_height (default: 0) | | block_timestamp | u64 | no | Unix seconds for ctx.block_timestamp (default: current wall time) |
Response:
{
"status": "ok",
"contract_id": "my-token-001",
"entrypoint": "transfer",
"returns": ["true"],
"effects": {
"emits": [
{ "name": "Transfer", "args": ["wallet:alice", "wallet:bob", "500"] }
],
"moves": [
{ "intrinsic": "send", "args": ["500", "wallet:alice", "wallet:bob"] }
],
"posts": [],
"writes": [
{ "path": ["balances", "wallet:alice"], "value": { "Integer": "4500" } },
{ "path": ["balances", "wallet:bob"], "value": { "Integer": "1500" } }
],
"deletes": []
},
"cost": {
"total_cost_units": 14,
"storage_reads": 4,
"storage_writes": 2,
"storage_deletes": 0,
"event_emits": 1,
"move_ops": 1,
"post_ops": 0
}
}On failure, the endpoint returns HTTP 400 with the TREA error message:
HTTP 400
"[LE-105] Contract simulation error: require failed: insufficient_balance"What simulation does and does not do
| Property | Behavior | | -------- | -------- | | State commit | None — storage, balances, and nonce are unchanged | | Ledger entry | Not created — no AEC segment, no shard write | | Event log | Not updated — emits appear only in the response | | Nonce | Not incremented | | Fee | Not charged | | Receipt | Not generated — simulation never writes to receipts.redb; see Execution Receipts | | Cross-contract | Supported — sub-calls execute with live storage but also without commit | | Lifecycle check | Not enforced — simulation works on contracts in any lifecycle state | | Signature | Not required — no SignedTransaction |
Argument format
Arguments mirror the ContractArg schema used in ContractCall transactions:
| Variant | JSON | TREA type | | ------- | ---- | --------- | | { "Bool": true } | boolean | bool | | { "Integer": "1000" } | string-encoded integer | u128, u64, … | | { "Address": "wallet:alice" } | string | Address | | { "Text": "hello" } | string | str |
Example — preview a token transfer
curl -s -X POST http://localhost:3001/api/contracts/my-token-001/simulate \
-H 'Content-Type: application/json' \
-d '{
"entrypoint": "transfer",
"args": [
{ "Address": "wallet:bob" },
{ "Integer": "500" }
],
"caller": "wallet:alice"
}' | jq .Check that effects.moves shows the expected transfer and returns is ["true"] before asking the user to sign the real transaction.
Example — preview an emit-only function
curl -s -X POST http://localhost:3001/api/contracts/my-token-001/simulate \
-H 'Content-Type: application/json' \
-d '{
"entrypoint": "approve",
"args": [
{ "Address": "wallet:spender" },
{ "Integer": "1000" }
],
"caller": "wallet:owner"
}' | jq .effects.emitsDesign notes
- Live storage, no commit. The simulation reads the current contract storage
from State and runs the full IR execution. Writes produced by the execution are discarded — the contract's StoredContractState in the ledger is never updated.
- Cost is informational. The
costblock shows gas-equivalent units so UIs
can display a fee estimate. The cost is not deducted from any account.
- Cross-contract calls. If the simulated entrypoint calls another contract
via CallCross, the sub-call is also executed with live storage but without commit. Depth limits and interface checks apply normally.
- Not a substitute for the real call. Race conditions can occur: between
simulation and the real transaction, another transaction may change storage. Always re-validate on submission.