Skip to main content

Overview

cowboy_sdk.runtime is the supported, developer-facing wrapper over the low-level pvm_host bindings. Actors should call runtime.* (and the higher-level self.storage, call(), send() helpers) rather than pvm_host directly. All listed signatures are from the shipping SDK.

Execution context

Mode helpers: mode() returns "fsm" (production), "checkpoint" (debug-only), or "local"; is_production() / is_development() / is_checkpoint_mode() are the boolean forms; require_fsm() raises unless running in FSM mode.

State

Prefer self.storage[...] (the @actor proxy, with automatic CBOR encoding). The raw layer underneath:
Integers are capped at 2^64 - 1. The canonical CBOR encoder emits no bignum tags, so anything at or above 2^64 fails to encode. Which exception you see depends on the path: self.storage[...] calls codec.encode directly and raises OverflowError, while emit_event payloads and handler returns go through payload_codec._encode_value, which reclassifies it to CodecError. The encoder decodes bignum tags 2 and 3, so a value can be read back that could never have been written — the asymmetry only surfaces on the write.This bites when a native integer holds a protocol quantity that is wider: CIP-20 token amounts are u128. An uncaught failure is safe — the transaction overlay discards the frame, so token and storage effects from earlier in the handler roll back with it — but it costs the gas already spent and surfaces as an opaque encode error rather than something the caller can act on. Range-check such values up front and return an explicit error instead.Note the contrast with an ordinary return {"error": ...}, which is a normal return and therefore commits everything the handler did before it. Validate before you mutate on those paths, or an early-out leaves half the work applied.

Events and gas

Gas is metered for you. The runtime and host account for the Cycles a handler consumes and the Cells its data occupies, and enforce both budgets; actor code does not participate in that accounting. Write handlers as if gas accounting does not exist, and use estimate_gas against a node when you need real cost figures.
Do not call runtime.charge_gas() in application actors. It is still present and still deducts Cycles from your budget, so a manual call is not a harmless annotation — it burns gas on top of the automatic metering, inflating the cost of every invocation and risking a spurious OutOfGas. Manual calls were removed from the bundled examples in node#921, and from the remaining actors, examples, and guides in cowboy#284 and node#1174 — diff those for what the removal looks like in practice.

Ownership and lifecycle

Messaging and jobs

Timers (CIP-5)

Event subscriptions (CIP-29)

Randomness and crypto

Code upgrade

Tokens (CIP-20)

The token surface (token_create, token_balance_of, token_total_supply, token_transfer, token_approve, token_allowance, token_transfer_from, token_mint, token_burn) is covered with examples in the CIP-20 spec; amounts are int base units (u128), addresses 20 bytes, token_id 32 bytes.

Behavior under the mock host

Unit tests run against cowboy_sdk.mock_host (usually via cowboy_sdk.testing.SimulatedChain). Context, state, ownership, timers, subscriptions, and call_actor (with set_call_handler) all work in-memory. emit_event works under SimulatedChain (which captures events for chain.events()) but not under the bare mock host; the mock implements no gas meter, so gas is not simulated in unit tests — use estimate_gas against a real node when you need cost figures. keccak256 falls back to SHA-256, and randomness and the token_* operations require a real chain.

Further reading

  • CIP-6 §12 — Runtime Module — normative spec for the core of this surface (mode helpers, CIP-29 subscriptions, and some context getters are shipping-SDK additions beyond §12)
  • SDK Overview — the high-level @actor / continuation / model layer
  • PVM Reference — validation rules and determinism guards