Sandbox & Inner Computer
Contract methods run inside a restricted SES compartment. The only chain-facing API available to that code is the InnerComputer (computer global): a read-only, fail-closed view of confirmed blockchain state.
For the full method reference, stabilizers, and confirmation rules, see Querying inside of a Contract.
Goals
- Determinism — If a query succeeds against a chain prefix, the same call must succeed with the same result on every extension of that chain.
- Fail closed — Transient facts (mempool, “not yet”, future heights) never become part of a valid transition.
- No silent soft-fail — Catching a thrown error does not clear invalidation; after the compartment returns, the host still rejects the transition if the evaluation was marked invalid.
Observation stability
For any InnerComputer method m and arguments args: if computer.m(...args) succeeds without invalidation against chain state b₁, then on any extension b₂ ⊇ b₁ the same call must succeed and return the same value.
Equivalently: every successful observation is invariant under future chain growth.
Confirmed locations (summary)
Most APIs require the referenced transaction to be in a block before the call may succeed:
latest is not exposed inside contracts (the live tip is non-deterministic under chain extension).
Invalidation flow
- A query fails or observes a transient fact (or a direct policy rule rejects, e.g. future height).
- InnerComputer marks the current evaluation frame invalid and throws. There is no per-instance invalid flag and no contract-facing invalidation API. Each
Db.eval/Modules.loadruns underwithEvalInvalidation(implementation lives in a dedicated eval-frame module):- Node:
AsyncLocalStorageviaprocess.getBuiltinModule('async_hooks')(no staticnode:async_hooksimport, so browser bundles stay clean). Concurrent evaluations are truly concurrent and isolated by async context. - Browser: await-scoped stack with serialized root frames (no Promise patching under SES
lockdown). Nested frames (e.g.Modules.loadinsideDb.eval) still nest; concurrent root evals queue so stack tops never cross-talk.
- Node:
- The host sets the active observation client on the frame. Free-variable
computermethods route to that client for the evaluation, so create-time SES bindings and the eval-time client share one observation identity. Invalidation always writes the active frame. - The compartment may return after
catch— the frame flag is not cleared until the host has checked it. - The host accepts or rejects using only
frame.invalid/frame.msg. If the compartment throws or returns after catch-and-continue,Db.evalstill rejects when the frame is marked invalid. - The in-compartment
computeris a hardened query-only facade: public observation methods only (noisInvalid/errorMsg/resetInvalid, no internal client object, methods not replaceable).
console endowment (dev only)
Compartment globals include host console only when the client mode is dev or debug.
In prod, console is not endowed. Contract or module code that references console gets a ReferenceError and the evaluation fails. Do not use console in production smart contracts — logging is not part of the on-chain API, and endowing the shared host console is ambient authority (especially without SES lockdown, which runs in prod).
Use off-chain tooling and the outer Computer client for diagnostics.
Error message shape
Every invalidation path (policy _invalidate, missing/nullish _safeCall, and host rethrow from Db.eval) builds the public string with a shared formatter so callers always see exactly one copy of:
Accessing non-existent on-chain state inside a smart contract is forbidden.
A short reason may appear before that suffix, for example:
getBlockHash with future height is forbidden. Accessing non-existent on-chain state inside a smart contract is forbidden.Transaction id … not found or not yet confirmed by sync. Accessing non-existent on-chain state inside a smart contract is forbidden.
The formatter is idempotent (already-suffixed strings are not doubled). Match with message.endsWith(...) (or equivalent). Do not expect a short policy reason alone without the standard suffix.
Client vs contract computer
Security notes (what contracts cannot do)
- Contracts cannot create or clear eval frames (
withEvalInvalidationis host-only). - The endowment exposes query methods only — there is no
isInvalid,errorMsg,resetInvalid, orresetGlobalInvalidfor contracts to call or shadow. - The endowment is hardened so contracts cannot replace
sync/first/ … or reassign the prototype to hide invalidation. - Host reject decisions use the frame only.
- In
prod, contracts cannot useconsole(not endowed). Prefer no logging in on-chain code at all.
Practical implications
- Confirm module deploys before contract
load. - Confirm object revisions before history walks or escrow audits.
- For terminal
lastchecks, spend the tip (e.g.delete) and wait for confirmation. - Stabilize in-contract TXO queries with a historical height or block hash; empty result sets with a valid stabilizer are fine (wait for indexing if apps/tests expect a known object to appear).
- Escrow / chess flows: cancel or settle, wait for confirmation, then
withdraw/ refund (cancel and withdraw cannot be one atomic observation of unconfirmed tip spend). - Do not ship contract methods that call
console.*if they must run undermode: 'prod'.