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
| Function | Returns |
|---|---|
context() | Full context dict: block_height, block_hash, tx_hash, sender, actor_addr, timestamp_ms, nonce, msg_id |
get_sender() | Transaction sender (20-byte address) |
get_actor_address() | This actor’s own address (20 bytes) |
get_tx_hash() | Current transaction hash (32 bytes) |
get_block_height() | Current block height (int) |
get_timestamp_ms() | Block timestamp in milliseconds |
get_chain_id() | Chain ID (falls back to 0 where the host context doesn’t supply one — the production context currently doesn’t) |
get_pvm_version() | PVM version string (falls back to "unknown" under the production host) |
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
Preferself.storage[...] (the @actor proxy, with automatic CBOR encoding). The raw layer underneath:
| Function | Semantics |
|---|---|
get_state(key: bytes) -> bytes | None | Read raw bytes |
set_state(key: bytes, value: bytes) | Write raw bytes; refuses the reserved keys __OWNER__ / __INITIALIZED__ |
delete_state(key: bytes) | Delete; same reserved-key guard |
scan_state_prefix(prefix: bytes, limit: int = 100) -> list[tuple[bytes, bytes]] | Up to limit key/value pairs from prefix, ascending; only verbatim (≤ 32-byte) keys are visible. Paginate by appending b"\x00" to the last key |
Events and gas
| Function | Semantics |
|---|---|
emit_event(name: str, payload: bytes | str | dict) | Emit an event; str is UTF-8-encoded, dict is JSON-encoded |
charge_gas(amount: int) | Report extra gas consumption to the meter |
Ownership and lifecycle
| Function | Semantics |
|---|---|
get_owner() -> bytes | Owner address, or b"" if unset |
assume_ownership_if_unowned() -> bytes | Bootstrap helper: if unowned, the current sender becomes owner (idempotent). Call it from init only — init runs atomically with deploy, which is what closes the front-running window |
transfer_ownership(new_owner: bytes) | First setter wins while unowned; afterwards only the owner may transfer. Zero address rejected |
renounce_ownership() | Permanently sets the owner to the zero address; @callable_by(OWNER) handlers become unreachable |
is_initialized() -> bool | True once init has completed successfully |
Messaging and jobs
| Function | Semantics |
|---|---|
call_actor(target: bytes, method: str, payload: bytes, cycles_limit: int) -> bytes | Synchronous cross-actor call (prefer the cowboy_sdk.call() wrapper, which handles CBOR and errors) |
send_message(target: bytes, payload: bytes) | Fire-and-forget async message — allowed in @deferred handlers only |
submit_job(payload: bytes) | Submit a pre-encoded Runner job — @deferred handlers only. Prefer the high-level runner.llm/http/mcp continuations (CIP-2) |
Timers (CIP-5)
| Function | Semantics |
|---|---|
schedule_timer(height: int, payload: bytes) -> bytes | Fire payload back at this actor at height; protocol defaults for fees/TTL; returns a timer_id |
schedule_timer_ex(height, payload, fee_payer=None, gas_limit=None, expires_at=None) -> bytes | Extended form: choose the fee payer — which must be the executing actor or the current transaction sender; any other address is rejected (CIP-5 §4.2) — plus a per-fire gas limit and an absolute expiry (≤ current height + max TTL) |
extend_timer(timer_id: bytes, new_expires_at: int) | Renew a timer you own; a missing timer is a silent no-op |
cancel_timer(timer_id: bytes) | Cancel by id |
Event subscriptions (CIP-29)
| Function | Semantics |
|---|---|
subscribe_event(emitter: bytes, topic: bytes, handler: str, gas_prepaid: int, bid: int = 0) -> bytes | Subscribe to (emitter, topic); registration fee and bid are burned, gas_prepaid is held and refundable; returns a 33-byte sub_id |
unsubscribe_event(sub_id: bytes) -> int | Subscriber-only; refunds remaining prepaid gas |
force_unsubscribe_event(sub_id: bytes) | Emitter-only; remaining gas refunds to the subscriber |
update_bid(sub_id: bytes, additional_bid: int) -> int | Raise the subscription’s priority bid (burned) |
topup_subscription(sub_id: bytes, additional_gas: int) -> int | Add prepaid gas; any caller may pay |
Randomness and crypto
| Function | Semantics |
|---|---|
randomness(domain: bytes) -> bytes | Deterministic protocol randomness, domain-separated |
keccak256(data: bytes) -> bytes | Keccak-256 digest |
Code upgrade
| Function | Semantics |
|---|---|
upgrade_self(new_code: bytes, new_manifest: bytes | None = None) | Replace this actor’s code (requires the sys.upgrade entitlement). A new manifest must be a subset of the current one — entitlements can narrow, never broaden. State, balance, and mailbox are preserved; new code takes effect on the next invocation |
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 againstcowboy_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 guard charge_gas calls behind runtime.is_production() in code you unit-test. 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

