gl_call Messages#
EmitExternalMessage Message#
Sends transaction to Ethereum address with optional value transfer.
Payload#
{
"EmitExternalMessage": {
"address": Address, // 20-byte target address
"calldata": Bytes, // EVM calldata
"value": U256 // Wei to transfer
}
}
Requirements#
Sufficient contract balance for value transfer
ExternalCall Message#
Calls Ethereum contract method (read-only operation).
Payload#
{
"ExternalCall": {
"address": Address, // 20-byte target contract address
"calldata": Bytes // EVM calldata
}
}
Requirements#
CallContract Message#
Calls another GenLayer Intelligent Contract.
Payload#
{
"CallContract": {
"address": Address, // 20-byte target contract address
"calldata": Calldata, // Method call in calldata format
"storage_view": Number, // Storage view: 0=default, 1=latest_finalized, 2=latest_decided
"catch_vm_error": Bool // optional (default false): take a VM error as the result
}
}
Requirements#
calldatasatisfies Method Calling Convention
Creates a sub-VM. See Sub-VM Creation.
A sub-VM that ends in a VMError normally ends its caller
too. With param catch_vm_error set, the caller reads that error as the
call’s result instead. A fatal VMError is never caught: the flag
does not apply to it, and the caller ends regardless.
Before every call, GenVM asks the host whether to delegate the callee to another executor. A null answer preserves the local path, including invalid_contract major_mismatch. A non-null answer executes the same derived sub-VM in the selected executor. The call result and its contribution to the execution hash are the same kinds of observable output in either path. Routing cycles are permitted and remain bounded by vm_recursion.
The calling convention is checked in the calling sub-VM, before the
callee is spawned. A violation is the caller’s own malformed argument, so it is
answered with Errno::Inval like the other argument checks and the caller can
recover — unlike a top-level entry, where the same violation is the execution’s
result (Entry Payload Validation).
EmitInternalMessage Message#
Posts message to GenLayer contract for later execution.
When GenVM forwards this message it derives Call Key from the
method field of calldata and attaches it to the emitted message.
Call Key is the function-selector
analog used to identify the target method.
Payload#
{
"EmitInternalMessage": {
"address": Address, // 20-byte target contract address
"calldata": Calldata, // Method call in calldata format
"value": U256, // Wei to transfer
"on": String, // "finalized" or "decided"
"use_balance": Bool, // optional (default false), see below
"fee_params": FeeParams // optional (default absent), required iff use_balance
}
}
Requirements#
Sufficient contract balance for value transfer
When
use_balanceis set: can_use_balance_for_message_fees andfee_paramscalldatasatisfies Method Calling Convention
Allocation-funded fees#
For an allocation-funded internal message, declaredBudget is the metered
primary reserve plus the budgets of the direct children in its emitted allocation
subtree. The same amount is deducted from the matching allocation and the
transaction’s message-fee pool. Grandchild budgets are already contained within
their direct parent’s budget and are not added again. The primary reserve includes
the consensus developer and DAO gross-up on its time-unit portion; the execution
budget is not grossed up. This formula is identical for messages emitted on
acceptance and finalization; the primary reserve already covers the child’s
configured lifecycle, including appeals
Balance-funded fees#
By default an outgoing internal message’s fee is drawn from the sender’s
prefunded message-fee pool and matched against the transaction’s allocation
tree. Setting use_balance (the chain’s useBalance) instead funds the fee
from the emitting contract’s own balance. fee_params carries the child
transaction’s fee configuration and mirrors the chain’s
InternalMessageFeeParams:
FeeParams {
"leader_time_units_allocation": U256, // per-round leader time units
"validator_time_units_allocation": U256, // per-round validator time units
"execution_budget_per_round": U256, // unified budget per leader round
"rotations": [U256], // per-round rotations; non-empty.
// rotations[0] is the initial round,
// the rest are appeal rounds
// (appealRounds = len - 1)
"max_price_gen_per_time_unit": U256, // GEN price cap; funding multiplier
"storage_fee_max_gas_price": U256, // storage price cap (revert guard)
"receipt_fee_max_gas_price": U256 // receipt price cap (revert guard)
}
Semantics:
The fee is metered from
fee_paramsand that metered amount becomes the child transaction’sdeclaredBudget— the contract balance is the only bound. The consensus term is charged at the guest’smax_price_gen_per_time_unitcap (matching the chain’sminMessagePrimaryFees), not the node’s livegenPerTimeUnit, so the fee scales with the cap. The consensus developer and DAO gross-up applies to this time-unit portion but not the execution budgetThe message is excluded from allocation matching, so no matching node is required (and none is consulted).
The contract must be able to cover
value + metered_feefrom its balance; otherwise the call fails withInsufficientBalance.The emitted allocation subtree is empty: nesting is fail-closed, so a child message must itself set
use_balanceor it fails to fund.
fee_params is validated before metering. The following are rejected with
Inval:
use_balancewithoutfee_params, orfee_paramswithoutuse_balance.Empty
rotations(appealRoundswould underflow).A zero
max_price_gen_per_time_unit,storage_fee_max_gas_priceorreceipt_fee_max_gas_price(the chain revertsFeeValueMustBeNonZeroat reveal).Out-of-bounds magnitudes: prices and budgets (
max_price_gen_per_time_unit,storage_fee_max_gas_price,receipt_fee_max_gas_price,execution_budget_per_round) must be below 296; counts (leader_time_units_allocation,validator_time_units_allocation, eachrotationsentry) below 232. These bounds keep the metered floor withinU256.
Metering additionally enforces node-configured bounds, surfaced as VMErrors:
fee below_minimum — either a non-zero
execution_budget_per_roundbelownode.messageBudgetFloor(the chain’sBudgetTooLow), or — unless both time-unit allocations are zero — a leader allocation outsidenode.minProposeTimeoutthroughnode.maxProposeTimeoutor a validator allocation outsidenode.minCommitTimeoutthroughnode.maxCommitTimeout.fee too_many_rounds —
rotationsimplies more consensus rounds than the node’s validator table supports (on-chainMAX_ROUNDS).
EmitInternalDeployMessage Message#
Deploys new intelligent contract to blockchain.
Payload#
{
"EmitInternalDeployMessage": {
"calldata": Calldata, // Constructor arguments in calldata format
"code": Bytes, // Contract bytecode
"value": U256, // Wei to transfer
"on": String, // "finalized" or "decided"
"salt_nonce": U256, // Salt for CREATE2-style deterministic addressing
"use_balance": Bool, // optional (default false)
"fee_params": FeeParams // optional (default absent), required iff use_balance
}
}
Requirements#
Sufficient contract balance for value transfer
When
use_balanceis set: can_use_balance_for_message_fees andfee_paramscalldatasatisfies Method Calling Convention
Supports CREATE2-style deployment with salt nonce for deterministic addressing.
use_balance / fee_params behave as for Balance-funded fees.
RunNondet Message#
Executes non-deterministic code with leader/validator consensus. See Switching To gvm-def-non-det-mode and Sub-VM Creation.
Payload#
{
"RunNondet": {
"data_leader": Bytes, // Code/data for leader execution
"data_validator": Bytes, // Code/data for validator execution
"runner": String, // optional (default "contract"): runner to execute
"custom_runners": [String], // optional (default absent): custom runners to grant
"catch_vm_error": Bool // optional (default false): take a VM error as the result
}
}
Requirements#
Semantics#
Creates a non-deterministic sub-VM. Derivation of its meta-properties,
including the param runner and param custom_runners semantics, is
specified in Sub-VM Creation.
A sub-VM that ends in a VMError normally ends its caller
too. With param catch_vm_error set, the caller reads that error as the
call’s result instead. A fatal VMError is never caught: the flag
does not apply to it, and the caller ends regardless.
Sandbox Message#
Executes code in a sandboxed environment. See Sub-VM Creation.
Payload#
{
"Sandbox": {
"data": Bytes, // Code/data for sandbox execution
"runner": String, // runner to execute; becomes the child's "contract"
"allow_write_storage": Bool, // Whether to allow storage writes
"allow_send_messages": Bool, // Whether to allow sending messages
"custom_runners": [String], // optional (default absent): custom runners to grant
"changes_on_error": String // fate of the child's changes on a non-return
}
}
Semantics#
Creates a sub-VM at the caller’s determinism level. Derivation of its
meta-properties, including the param runner and param
custom_runners semantics, is specified in
Sub-VM Creation.
The caller receives the sandbox result (Sub-VM Result Encoding) and may handle both VMError and UserError. If the sandbox terminates with Fatal VM Error, the caller terminates with the same fatal VM error instead.
param changes_on_error says what becomes of the storage writes and
emissions of a sandbox that does not Return. "inherit" is
its only accepted value: the caller keeps them, exactly as it keeps the ones it
made itself. Any other value is a malformed message
RegisterRunner Message#
Registers a runner archive at runtime, making it available under the
custom:<hash> runner id. The <hash> is the SHA3-256 of the supplied
code encoded with GVM32 (Base32).
See Custom Runner Loading.
Payload#
{
"RegisterRunner": {
"code": Bytes // runner archive (zip, raw wasm or commented text)
}
}
Requirements#
Semantics#
RegisterRunner performs a load action for custom:<hash> in the calling
sub-VM. If the runner is already loaded, registration is a free no-op
that returns the same runner id. Otherwise
runner_load_cost plus code length is charged against
the caller’s RAM budget before the archive is parsed; on success, the runner
also incurs its metadata charge and enters
the caller’s loaded set
The outcomes are:
Missing Deterministic Mode: the call fails with
Forbidden. Nothing is charged and no state changes.Insufficient memory for the base cost and
codelength: the sub-VM exits with out_of memory. Nothing is charged and the runner is not registeredInsufficient memory for metadata while loading: the sub-VM exits with out_of memory and the runner is not registered
Malformed archive: the call fails with a deterministic invalid-contract VMError. The charge is retained until the sub-VM finishes, and the runner is not in the loaded set.
Success: the runner id is returned and the runner is in the caller’s loaded set.
MapFile Message#
Maps a file from a runner into the VM filesystem at runtime, behaving the same as
the MapFile runner action (see the Python SDK runners documentation). If
path_in_runner ends with / the whole directory subtree is mapped.
Payload#
{
"MapFile": {
"runner": String, // runner id (name:hash, contract, chain:..., custom:...)
"path_in_runner": String, // path within the runner archive
"path_in_vfs": String // absolute destination path in the VM filesystem
}
}
Mapping into /vm/ is forbidden. See
Custom Runner Loading for custom: runner resolution.
Resolving param runner performs a load action for that runner. The load is
charged on first load in this sub-VM and is free if the runner is
already loaded.
WebRender Message#
Renders web content using GenVM web module.
Payload#
{
"WebRender": {
"mode": String, // "text", "html", or "screenshot"
"url": String, // URL to render
"post_load_wait": String // Wait duration, e.g. "5s" or "500ms"
}
}
Requirements#
Non-Deterministic Mode execution
Web module availability
WebRequest Message#
Makes HTTP requests using GenVM web module.
Payload#
{
"WebRequest": {
"method": String, // "GET", "POST", "HEAD", "DELETE", "OPTIONS", or "PATCH"
"url": String, // Request URL
"headers": Map, // String -> Bytes mapping of headers
"body": Bytes | null, // Optional request body
"sign": Bool // Whether to sign the request (default: false)
}
}
Response#
{
"status": Number, // HTTP status code
"headers": Map, // String -> Bytes mapping of response headers
"body": Bytes // Response body
}
Requirements#
Non-Deterministic Mode execution
Web module availability
ExecPrompt Message#
Executes LLM prompts using GenVM LLM module.
Payload#
{
"ExecPrompt": {
"response_format": String, // "text" (default) or "json"
"prompt": String, // The prompt text
"images": Array // Array of image bytes (max 2)
}
}
Requirements#
Non-Deterministic Mode execution
LLM module availability
Supports up to 2 images per prompt. Consumes fuel based on LLM usage.
ExecPromptTemplate Message#
Executes structured LLM prompt templates with type-specific validation.
Payload#
One of the following template types:
// Comparative template (expects boolean response)
{
"ExecPromptTemplate": {
"template": "EqComparative",
"leader_answer": String,
"validator_answer": String,
"principle": String
}
}
// Non-comparative validator template
{
"ExecPromptTemplate": {
"template": "EqNonComparativeValidator",
"task": String,
"criteria": String,
"input": String,
"output": String
}
}
// Non-comparative leader template
{
"ExecPromptTemplate": {
"template": "EqNonComparativeLeader",
"task": String,
"criteria": String,
"input": String
}
}
Requirements#
Non-Deterministic Mode execution
LLM module availability
Comparative templates expect boolean responses. Non-comparative templates expect text responses.
EmitEvent Message#
Emits blockchain events with topics and data.
Payload#
{
"EmitEvent": {
"topics": Array, // Array of 32-byte topics (max 4)
"blob": Map // String -> Calldata mapping of event data
}
}
Requirements#
GenVM version 0.1.5 or higher
Topics must be exactly 32 bytes each.
UserError Message#
Triggers contract UserError with custom error message.
Payload#
{
"UserError": Any // Error message
}
Causes VM to exit with UserError. Terminates contract execution immediately.
Return Message#
Returns value from contract execution and terminates.
Payload#
{
"Return": Calldata // Return value in calldata format
}
Causes VM to exit with ContractReturn. Encodes return value using
Calldata Encoding format.
Implementation Discretion#
Where a message below says an implementation may ignore it, that discretion covers only the side effect the message asks for — writing a log line, recording a timing. The value returned to the guest is never at an implementation’s discretion: it is fixed by this specification for the mode the call is made in, and such a call MUST NOT fail. A message whose result differs between two conformant implementations would make Deterministic Mode execution diverge across validators; widening or narrowing what a message returns therefore requires a new GenVM version.
Trace.Message Message#
Logs a debug message with timing information including:
Custom message text
Total elapsed time since VM start
Time elapsed since last trace call
Payload#
{
"Trace": {
"Message": String // Debug message text
}
}
Note
Whether anything is logged is implementation-defined (see Implementation Discretion); the call returns no value and always succeeds.
Requirements#
GenVM version 0.1.10 or higher
Trace.RuntimeMicroseconds Sub-Message#
In Non-Deterministic Mode returns the elapsed execution time in microseconds since VM start.
In Deterministic Mode, it returns 0 — exposing real elapsed time there
would break determinism. An implementation MAY support a debug mode that
returns real elapsed time instead; such a mode is for local debugging only and
MUST NOT be used on a consensus network.
Payload#
{
"Trace": "RuntimeMicroseconds"
}
Note
The returned value is not at an implementation’s discretion — in
Deterministic Mode it is exactly 0 unless an implementation’s debug
mode permits real elapsed time, and the call never fails (see
Implementation Discretion).
Requirements#
GenVM version 0.1.10 or higher
Yield Message#
Cooperative yield. Currently a no-op and returns no value; it is reserved for future use in waiting loops.
Payload#
{
"Yield": null
}
Note
Whether the implementation actually yields anything is implementation-defined (see Implementation Discretion); the call returns no value and always succeeds.
Requirements#
GenVM version 0.3.0 or higher
GetTimestamp Message#
Returns the current timestamp as the number of seconds since the Unix epoch.
In Deterministic Mode it returns the transaction timestamp, keeping the value deterministic across validators. In Non-Deterministic Mode it returns the real wall-clock time.
Payload#
{
"GetTimestamp": null
}
Returns the timestamp encoded as a Calldata Encoding number.
Requirements#
GenVM version 0.3.0 or higher