GenLayer Node API
The GenLayer Node provides a JSON-RPC API (opens in a new tab) for interacting with it. This API allows you to execute contract calls, retrieve transaction information, and perform various blockchain operations.
GenLayer Methods
gen_call
Executes a new message call immediately without creating a transaction on the blockchain. This method supports read operations, write operations, and contract deployments.
Method: gen_call
Parameters:
request(object, required): The contract call request parametersfrom(string, required): The address making the callto(string, required): The contract address to calldata(string, required): The encoded function call datatype(string, required): The type of call ("read","write", or"deploy")blockNumber(string, optional): The block number to query at (hex-encoded). If not provided, uses latest blockstatus(string, optional): The transaction status filter ("finalized"or"decided"). If not provided, uses decided statevalue(string, optional): The value to send with the call (hex-encoded)leader_public_data(string or null, optional): Hex-encoded data returned by a previous leader execution. Its encoding is defined by the GenVM specification. When provided, GenVM runs in validator mode; when omitted or null, GenVM runs in leader mode
Returns: A structured JSON object containing execution results and debug information
Response Fields:
data(string): Hex-encoded return value from contract executionleaderPublicData(string): Hex-encoded leader data whose encoding is defined by the GenVM specification. Populated in leader mode; empty in validator modestatus(object): Execution statuscode(number): Status code (0=success, 1=user error, 2=vm error, 3=internal error)message(string): Human-readable status message
stdout(string): Standard output captured during executionstderr(string): Standard error captured during executionlogs(array): GenVM log entries, each as a JSON object with fields likelevel,message,target,file,tsevents(array): Contract events emitted during executiontopics(array of strings): Event topics as hex-encoded stringsdata(string): Event data as hex-encoded string
messages(array): Messages emitted by the contractmessageType(number): Type identifier for the messagerecipient(string): Recipient addressvalue(string): Value in hex formatdata(string): Message data as hex-encoded stringonAcceptance(boolean): Whether message is sent on acceptance. Alwaysfalsefor an external message (messageType0): external messages on acceptance are not supported yetsaltNonce(string): Salt nonce in hex format
nondetDisagreementCallNo(number or null): When running in validator mode (withleader_public_data), indicates whichrun_nondetcall the validator disagreed on. Null when there is no disagreement or when running in leader modesyncedBlock(string): Hex-encoded block number that GenVM has synced to. Indicates data freshness — responses are based on state up to this block
Example - Deploy Call:
{
"jsonrpc": "2.0",
"method": "gen_call",
"params": [
{
"type": "deploy",
"data": "0xf9023db90223232076302e312e300a23207b2022446570656e6473223a202270792d67656e6c617965723a3135716669766a7679383038303072683939387063786d64326d38766131777132717a71687a3835306e3867676372346939713022207d0a0a66726f6d2067656e6c6179657220696d706f7274202a0a0a0a2320636f6e747261637420636c6173730a636c6173732053746f7261676528676c2e436f6e7472616374293a0a2020202073746f726167653a207374720a0a202020202320636f6e7374727563746f720a20202020646566205f5f696e69745f5f2873656c662c20696e697469616c5f73746f726167653a20737472293a0a202020202020202073656c662e73746f72616765203d20696e697469616c5f73746f726167650a0a20202020232072656164206d6574686f6473206d75737420626520616e6e6f7461746564207769746820766965770a2020202040676c2e7075626c69632e766965770a20202020646566206765745f73746f726167652873656c6629202d3e207374723a0a202020202020202072657475726e2073656c662e73746f726167650a0a2020202023207772697465206d6574686f640a2020202040676c2e7075626c69632e77726974650a20202020646566207570646174655f73746f726167652873656c662c206e65775f73746f726167653a2073747229202d3e204e6f6e653a0a202020202020202073656c662e73746f72616765203d206e65775f73746f726167650a950e04617267730d6c696e697469616c2076616c756500",
"from": "0xf6A60752C24Cd3BFcAf8d387a5EB62d3746F44EE",
"to": "0x0000000000000000000000000000000000000000",
"status": "finalized"
}
],
"id": 1
}Example Response (Deploy Success):
{
"jsonrpc": "2.0",
"result": {
"data": "00",
"leaderPublicData": "0e076e645f6f75747305",
"status": {
"code": 0,
"message": "success"
},
"stdout": "",
"stderr": "",
"logs": [
{
"file": "common/src/lib.rs:111",
"level": "info",
"message": "logging initialized",
"target": "genvm_common",
"ts": "1770221913210",
"version": "v0.2.11-aarch64-macos-release"
},
{
"file": "src/exe/run.rs:150",
"genvm_id": "1",
"level": "info",
"message": "genvm id",
"target": "genvm::exe::run",
"ts": "1770221913212"
},
{
"file": "src/lib.rs:299",
"level": "info",
"message": "metrics",
"metrics": {
"gvm": {
"host": { "time": 654 },
"llm_module": { "calls": 0, "time": 0 },
"supervisor": { "compilation_time": 0, "compiled_modules": 0, "precompile_hits": 2 },
"web_module": { "calls": 0, "time": 0 }
},
"llm": null,
"web": null
},
"target": "genvm",
"ts": "1770221913590"
}
],
"events": [],
"messages": [],
"nondetDisagreementCallNo": null
},
"id": 1
}Example - Read Call (Latest Decided State):
{
"jsonrpc": "2.0",
"method": "gen_call",
"params": [
{
"type": "read",
"data": "0xd8960e066d6574686f646c6765745f686176655f636f696e00",
"from": "0xf6A60752C24Cd3BFcAf8d387a5EB62d3746F44EE",
"to": "0xCe5712E63fd5475288aB1B7c0a368B9417357b81",
"gas": "0x5208",
"value": "0x0"
}
],
"id": 1
}Example - Read Call with Specific Block and Status:
{
"jsonrpc": "2.0",
"method": "gen_call",
"params": [
{
"type": "read",
"data": "0xd8960e066d6574686f646c6765745f686176655f636f696e00",
"from": "0xf6A60752C24Cd3BFcAf8d387a5EB62d3746F44EE",
"to": "0xCe5712E63fd5475288aB1B7c0a368B9417357b81",
"blockNumber": "0x151ec5",
"status": "decided"
}
],
"id": 1
}Example - Write Call:
{
"jsonrpc": "2.0",
"method": "gen_call",
"params": [
{
"from": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6",
"to": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6",
"data": "0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b8d4c9db96c4b4d8b6",
"type": "write",
"gas": "0x5208",
"value":"0x0"
}
],
"id": 1
}Example Response with Messages (Router Forward Call):
When a contract emits internal messages (e.g., a router contract forwarding calls), the response includes the messages array:
{
"jsonrpc": "2.0",
"result": {
"data": "00",
"leaderPublicData": "0e076e645f6f75747305",
"status": {
"code": 0,
"message": "success"
},
"stdout": "",
"stderr": "",
"logs": [
{
"file": "src/lib.rs:299",
"level": "info",
"message": "metrics",
"target": "genvm",
"ts": "1770225770000"
}
],
"events": [],
"messages": [
{
"messageType": 1,
"recipient": "0xc346f80c86adf1d71accfc4ef6efe7482b25be40",
"value": "0x0",
"data": "0xf86eb86a16046172677331d4023078393164463338654243623930384335643635...",
"onAcceptance": false,
"saltNonce": "0x0"
},
{
"messageType": 1,
"recipient": "0xad26513a4315b0ea8a5c800322b35092a83dfd3d",
"value": "0x0",
"data": "0xf86eb86a16046172677331d4023078333738653339374533316346383538333630...",
"onAcceptance": false,
"saltNonce": "0x0"
}
],
"nondetDisagreementCallNo": null
},
"id": 1
}Notes:
- The
blockNumberparameter can be either hex-encoded with a0xprefix (e.g.,"0x151ec5") or decimal (e.g.,"1384133") - Valid
statusvalues are"decided"(default) and"finalized" - When both parameters are omitted, the call executes against the latest decided state
- For
readandwritetype calls, if the contract doesn't exist at the specified block/status combination, an error is returned with code-32001(Resource not found) and message"contract not found at address 0x..." - For
deploytype calls, thestatusandblockNumberparameters are not used for contract state validation since no contract exists yet
gen_getContractSchema
Retrieve the schema/interface of a GenLayer contract to understand its available methods and properties.
Method: gen_getContractSchema
Parameters:
request(object, required): The contract schema requestcode(string, required): Base64-encoded contract code
Returns: Contract schema object with methods and properties
{
"ctor": { // constructor info
"kwparams": {}, // dict
"params": [["value", "type"]] // list of tuples
},
"methods": { // dict from method name to method info
"method_name": {
"kwparams": {}, // dict
"params": [["value", "type"]], // list of tuples
"payable": false, // bool
"readonly": false, // bool
"ret": "null" // string
},
}
}Example:
{
"jsonrpc": "2.0",
"method": "gen_getContractSchema",
"params": [
{
"code": "IyB7ICJEZXBlbmRzIjogInB5LWdlbmxheWVyOnRlc3QiIH0KCmZyb20gZ2VubGF5ZXIgaW1wb3J0ICoKCgojIGNvbnRyYWN0IGNsYXNzCmNsYXNzIFN0b3JhZ2UoZ2wuQ29udHJhY3QpOgogICAgc3RvcmFnZTogc3RyCgogICAgIyBjb25zdHJ1Y3RvcgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGluaXRpYWxfc3RvcmFnZTogc3RyKToKICAgICAgICBzZWxmLnN0b3JhZ2UgPSBpbml0aWFsX3N0b3JhZ2UKCiAgICAjIHJlYWQgbWV0aG9kcyBtdXN0IGJlIGFubm90YXRlZCB3aXRoIHZpZXcKICAgIEBnbC5wdWJsaWMudmlldwogICAgZGVmIGdldF9zdG9yYWdlKHNlbGYpIC0+IHN0cjoKICAgICAgICByZXR1cm4gc2VsZi5zdG9yYWdlCgogICAgIyB3cml0ZSBtZXRob2QKICAgIEBnbC5wdWJsaWMud3JpdGUKICAgIGRlZiB1cGRhdGVfc3RvcmFnZShzZWxmLCBuZXdfc3RvcmFnZTogc3RyKSAtPiBOb25lOgogICAgICAgIHNlbGYuc3RvcmFnZSA9IG5ld19zdG9yYWdlCg=="
}
],
"id": 1
}Response:
{
"jsonrpc": "2.0",
"result": {
"ctor": {
"kwparams": {},
"params": [["initial_storage", "string"]]
},
"methods": {
"get_storage": {
"kwparams": {},
"params": [],
"readonly": true,
"ret": "string"
},
"update_storage": {
"kwparams": {},
"params": [["new_storage", "string"]],
"payable": false,
"readonly": false,
"ret": "null"
}
}
},
"id": 1
}gen_getContractState
Retrieves the current state of a GenLayer intelligent contract at a specific block height and transaction status.
Method: gen_getContractState
Parameters:
request(object, required): The contract state request parametersaddress(string, required): The contract address to queryblockNumber(string, optional): The block number to query at (hex-encoded). If not provided, uses latest blockstatus(string, optional): The transaction status filter ("finalized"or"decided"). If not provided, uses decided state
Returns: Hex-encoded contract state data
Limits:
| Limit | Default | Config key |
|---|---|---|
| Contract bytes read per call | 16 MB | node.rpc.contract_read_max_bytes |
This method reads the contract's whole storage, so its cost grows with the contract. The limit stops the read once the contract exceeds it and returns an error, rather than returning part of the state as if it were all of it. Operators may reduce it; 0 or less disables it.
Example Request - Latest Decided State:
{
"jsonrpc": "2.0",
"method": "gen_getContractState",
"params": [
{
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f"
}
],
"id": 1
}Example Request - Specific Block and Status:
{
"jsonrpc": "2.0",
"method": "gen_getContractState",
"params": [
{
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f",
"blockNumber": "0x151ec5",
"status": "decided"
}
],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"result": "0xd9960e0773746f726167656c6e757064617465642074657374207374",
"id": 1
}Notes:
- The
blockNumberparameter should be hex-encoded with a0xprefix - Valid
statusvalues are"decided"(default) and"finalized" - The returned state is hex-encoded binary data with a
0xprefix - Empty contract state returns
"0x" - If the contract doesn't exist at the specified block/status, an error will be returned
cURL Example:
# Get latest decided state
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_getContractState",
"params": [{
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f"
}],
"id": 1
}'
# Get state at specific block with decided status
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_getContractState",
"params": [{
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f",
"blockNumber": "0x151ec5",
"status": "decided"
}],
"id": 1
}'gen_getContractCode
Retrieves the code of a deployed GenLayer intelligent contract at a specific block height and transaction status.
Method: gen_getContractCode
Parameters:
request(object, required): The contract code request parametersaddress(string, required): The contract address to queryblockNumber(string, optional): The block number to query at (hex-encoded). If not provided, uses latest blockstatus(string, optional): The transaction status filter ("finalized"or"decided"). If not provided, uses decided state
Returns: Base64-encoded contract code
Limits:
| Limit | Default | Config key |
|---|---|---|
| Contract bytes read per call | 16 MB | node.rpc.contract_read_max_bytes |
The node reads the contract's code slot directly, so the cost of a call is proportional to the code it returns rather than to everything the contract stores. The limit is a backstop above that: a contract whose declared code length exceeds it is refused before the bytes are read. Operators may reduce it; 0 or less disables it.
Example Request - Latest Decided State:
{
"jsonrpc": "2.0",
"method": "gen_getContractCode",
"params": [
{
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f"
}
],
"id": 1
}Example Request - Specific Block and Status:
{
"jsonrpc": "2.0",
"method": "gen_getContractCode",
"params": [
{
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f",
"blockNumber": "0x151ec5",
"status": "finalized"
}
],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"result": "IyB7ICJEZXBlbmRzIjogInB5LWdlbmxheWVyOnRlc3QiIH0KCmZyb20gZ2VubGF5ZXIgaW1wb3J0ICoKCgpjbGFzcyBTdG9yYWdlKGdsLkNvbnRyYWN0KToKICAgIHN0b3JhZ2U6IHN0cgoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBpbml0aWFsX3N0b3JhZ2U6IHN0cik6CiAgICAgICAgc2VsZi5zdG9yYWdlID0gaW5pdGlhbF9zdG9yYWdlCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X3N0b3JhZ2Uoc2VsZikgLT4gc3RyOgogICAgICAgIHJldHVybiBzZWxmLnN0b3JhZ2UKCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgdXBkYXRlX3N0b3JhZ2Uoc2VsZiwgbmV3X3N0b3JhZ2U6IHN0cikgLT4gTm9uZToKICAgICAgICBzZWxmLnN0b3JhZ2UgPSBuZXdfc3RvcmFnZQo=",
"id": 1
}Notes:
- The
blockNumberparameter should be hex-encoded with a0xprefix - Valid
statusvalues are"decided"(default) and"finalized" - The returned code is base64-encoded
- If the contract doesn't exist at the specified block/status, an error will be returned
- If the address does not have code (not a contract), an error will be returned
- If the contract's code exceeds
node.rpc.contract_read_max_bytes, the call is refused rather than truncated, so a partial answer is never returned as a whole one
cURL Example:
# Get latest decided code
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_getContractCode",
"params": [{
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f"
}],
"id": 1
}'
# Get code at specific block with finalized status
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_getContractCode",
"params": [{
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f",
"blockNumber": "0x151ec5",
"status": "finalized"
}],
"id": 1
}'gen_getTransactionReceipt
Retrieves the transaction receipt for a given transaction ID. This endpoint is a proxy to the underlying consensus layer's getTransactionAllData function, providing detailed information about a transaction that has been processed.
Method: gen_getTransactionReceipt
Parameters:
request(object, required):txId(string, required): The 32-byte transaction hash as a hexadecimal string with a0xprefix.
Returns:
A Transaction object which contains the details of the transaction. The structure is based on ITransactionsTransaction from the consensus contracts. The object has the following fields:
id(string): The transaction ID as a hex string.txOrigin(address): The original address that initiated the transaction.sender(address): The address of the sender.recipient(address): The destination address of the transaction.activator(address): The address of the validator that activated the transaction.status(uint8): The current status of the transaction.statusName(string): The ABI transaction status name forstatus(Uninitialized,Pending,Proposing,Committing,Revealing,Accepted,Undetermined,Finalized,Canceled,AppealRevealing,AppealCommitting,ValidatorsTimeout,LeaderTimeout, orLeaderRevealing).previousStatus(null): Always null. The contract dropped this field fromITransactions.Transaction(26 → 25 tuple components), so the node has nothing to report. It isnullrather than0because0decodes asUninitialized, which would be a wrong answer rather than an absent one. The field is removed in a v2 receipt.txSlot(uint256): The transaction slot number.initialRotations(uint256): The initial number of rotations.numOfInitialValidators(uint256): The number of initial validators.randomSeed(string): The random seed used for the transaction as a hex string.txExecutionHash(string): The execution hash of the transaction as a hex string.txCallData(string): The transaction call data as a hex string.eqBlocksOutputs(string): The equivalent blocks outputs as a hex string.timestamps(object): Object containing various timestamps:Created(uint256): When the transaction was created.Pending(uint256): When the transaction became pending.Activated(uint256): When the transaction was activated.Proposed(uint256): When the transaction was proposed.Committed(uint256): When the transaction was committed.LastVote(uint256): When the last vote was cast.AppealSubmitted(uint256): When an appeal was submitted.LeaderRevealed(uint256): When the leader was revealed.
result(uint8): The result of the transaction execution.txExecutionResult(uint8): The leader execution vote result (0NotVoted,1FinishedWithReturn,2FinishedWithError,3Timeout,4NondetDisagree,5DeterministicViolation).txExecutionResultName(string): The ABI vote type name fortxExecutionResult.epoch(uint256): The epoch the transaction belongs to.resultHash(string): The transaction result hash as a hex string.validUntil(uint256): The timestamp until which the transaction is valid.fees(object|null): Fee accounting echo.nullwhen fee accounting is disabled or the FeeManager binding/address is not available. All numeric values in this object are decimal strings.-
deposit(string): Submission-time fee custody sum, fromtransactionFeesBudget(txId) + transactionDeveloperFeesBudget(txId). -
userValue(string): User value carried by the transaction. -
distribution(object): Effective fee distribution for the transaction:leaderTimeunitsAllocation(string)validatorTimeunitsAllocation(string)appealRounds(string)executionBudgetPerRound(string)totalMessageFees(string)rotations(string[])maxPriceGenPerTimeUnit(string)storageFeeMaxGasPrice(string)receiptFeeMaxGasPrice(string)
-
locked(object|null): Activation-locked prices, ornullbefore activation when no locked prices exist:genPerTimeUnit(string)storageUnitPrice(string)receiptGasPrice(string)
-
consumed(object): On-chain consumption counters:executionConsumed(string)storageFeeUsed(string): Storage fee charged on-chain for the transaction, equal to the storage pages it consumed multiplied by the lockedstorageUnitPrice.messageFeesConsumed(string)messageFeesBudgetTotal(string)
-
hasAllocations(boolean):truewhen the chain pinned the transaction's message allocations: a pinned per-message fee allocation tree, or a closed transaction, whose allowed set is empty.falsefor an open bucket (global message-fee bucket) or fee-less transactions. It is alsofalsewhen the tree cannot be read, so that a receipt query still answers; the node logs the read failure. -
allocations(array): The pinned per-message fee allocation tree, one entry per locked allocation, internal and external (empty for an open bucket and for a closed transaction). Budget comes from the on-chain allocation tree;consumedis read from the separateallocationConsumed(txId, key)mapping, keyed bykeccak256(abi.encode(messageType, recipient, callKey)). Each entry:messageType(number):0= external,1= internal.onAcceptance(boolean): Emission phase the allocation is pinned to (true= on acceptance,false= on finalization).parentIndex(string): Index of the parent allocation in the flat tree, or the root sentinel (2^256 - 1) for a root-layer allocation.recipient(string): Target contract address the allocation funds. There is no recipient wildcard: the zero address is a concrete recipient.callKey(string): 32-byte call key (hex) the allocation matches, or thekeccak256("")call-key wildcard.budget(string): Maximum message-fee budget locked for this allocation.consumed(string): Amount consumed against this allocation so far.feeParams(string): ABI-encoded per-message fee params (hex), decoded permessageType.
-
allocationPolicy(string, optional): How the chain classifies the transaction's message allocations. It is read from the chain's own policy, not derived fromhasAllocationsandallocations; read it to tell a closed transaction from the other two.allocationPolicyhasAllocationsallocationsMeaning "open"false[]Nothing is pinned. Messages draw from the open bucket. "pinned"trueinternal and external nodes A message must match one of the listed allocations. "closed"true[]Allocations are pinned to an empty allowed set. The transaction runs and can fund a message from its own contract balance; the chain rejects every other message it emits. The field is omitted when the node could not read the allocation tree or the policy, or when the chain returns a policy this node version does not know; the node logs it. A receipt never states a policy it did not read.
The values are read at the chain head. The chain deletes the allocation marker when it refunds a transaction's fee budget, so such a transaction reads
"open"(andhasAllocations: false) whatever it was before. That applies to a cancelled transaction, to one that expired by idleness, and to one finalized through recovery; ordinary finalization keeps the policy. For those transactions"open"does not show that the transaction ran as an open bucket.
-
readStateBlockRanges(array): Array of block range objects, each containing:ActivationBlock(uint256): The activation block number.ProcessingBlock(uint256): The processing block number.ProposalBlock(uint256): The proposal block number.
roundData(array): Array of round data objects containing:round(uint256): The round number.leaderIndex(uint256): Index of the round leader.votesCommitted(uint256): Number of votes committed.votesRevealed(uint256): Number of votes revealed.appealBond(uint256): The appeal bond amount.rotationsLeft(uint256): Number of rotations remaining.result(uint256): The round result.roundValidators(array): Array of validator addresses for this round.validatorVotes(string): Base64-encoded validator votes.validatorVotesHash(array): Array of vote hashes as hex strings.validatorResultHash(array): Array of result hashes as hex strings.
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_getTransactionReceipt",
"params": [
{
"txId": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49"
}
],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49",
"txOrigin": "0x5b70759760a85d92bf972b67ee8558e8e8da87d4",
"sender": "0x5b70759760a85d92bf972b67ee8558e8e8da87d4",
"recipient": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f",
"activator": "0xf27b07d36ff5b9dcd37fd3a09811d6456dd4483a",
"status": 13,
"statusName": "LeaderRevealing",
"previousStatus": null,
"txSlot": 1,
"initialRotations": 1,
"numOfInitialValidators": 5,
"randomSeed": "0x3d0bdff63bce1cf84b155f1557bb15e303b903b1b559325b6492794a5e498954",
"txExecutionHash": "0x76744dc491b1725eb2ba08cf516855c14aac310e36711368c04442fd39d61853",
"txCallData": "f5b31604617267730da4017570646174656420746573742073746f72616765066d6574686f64747570646174655f73746f7261676500",
"eqBlocksOutputs": "c0",
"timestamps": {
"Created": 1750774777,
"Pending": 1750774777,
"Activated": 1750774781,
"Proposed": 1750774786,
"Committed": 1750774791,
"LastVote": 0,
"AppealSubmitted": 0,
"LeaderRevealed": 0
},
"result": 0,
"txExecutionResult": 3,
"txExecutionResultName": "Timeout",
"epoch": 12,
"resultHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"validUntil": 0,
"fees": {
"deposit": "1234500000000000",
"userValue": "0",
"distribution": {
"leaderTimeunitsAllocation": "100",
"validatorTimeunitsAllocation": "200",
"appealRounds": "0",
"executionBudgetPerRound": "20000000000000000",
"totalMessageFees": "0",
"rotations": ["0"],
"maxPriceGenPerTimeUnit": "1200000000000000",
"storageFeeMaxGasPrice": "12",
"receiptFeeMaxGasPrice": "300000000"
},
"locked": {
"genPerTimeUnit": "0",
"storageUnitPrice": "10",
"receiptGasPrice": "250000000"
},
"consumed": {
"executionConsumed": "0",
"storageFeeUsed": "250",
"messageFeesConsumed": "0",
"messageFeesBudgetTotal": "0"
},
"hasAllocations": false,
"allocations": [],
"allocationPolicy": "open"
},
"readStateBlockRanges": [
{
"ActivationBlock": 1377797,
"ProcessingBlock": 1377797,
"ProposalBlock": 1377798
}
],
"roundData": [
{
"round": 0,
"leaderIndex": 3,
"votesCommitted": 5,
"votesRevealed": 0,
"appealBond": 0,
"rotationsLeft": 1,
"result": 0,
"roundValidators": [
"0xa707a1d9dd946242f921f0ad62f61e1b7df30c85",
"0xf27b07d36ff5b9dcd37fd3a09811d6456dd4483a",
"0xf3738216c3208d77a9db79a1d31824e5133083c4",
"0x56394354538bff1e0ee5021c2b6d3522fce30257",
"0xcfdbac032f82f388d9eca76efa6610b98e7f59fe"
],
"validatorVotes": "AAAAAAA=",
"validatorVotesHash": [
"54e3478b02dfb009a35d46b19ac7c967b65416430a33d3860e9301a0f2b19d57",
"f74a48e7283c91757bf2c612e40f8a9eb69ea0f02cf5e6b89c3c9b6ad629a3c1",
"2f51c3b1d9ff35140260fb42330fbed12303a9915b8da786c9b8ed060983fa6c",
"730127e27167fcf2450fe506250b85d916fbf07dd874b7f0c69a2f8ceb9e44a6",
"473d57953f7e7c1a6fb1d2335618f21088025e02bd9fa30d6f85f20a55c24d15"
],
"validatorResultHash": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000"
]
}
]
}
}gen_getTransactionStatus
Returns the transaction's current materialized consensus status. This is the lightweight polling surface: it does not project deadlines or turn protocol actions into synthetic statuses.
Method: gen_getTransactionStatus
Parameters:
request(object, required):txId(string, required): The transaction hash (hex-encoded with0xprefix)
Returns: Object with status string and numeric code
Response Fields:
| Field | Type | Description |
|---|---|---|
status | string | Human-readable status name (e.g., "Finalized", "Accepted", "Pending") |
statusCode | uint8 | Numeric status code |
Status Codes:
| Code | Status |
|---|---|
| 0 | Uninitialized |
| 1 | Pending |
| 2 | Proposing |
| 3 | Committing |
| 4 | Revealing |
| 5 | Accepted |
| 6 | Undetermined |
| 7 | Finalized |
| 8 | Canceled |
| 9 | AppealRevealing |
| 10 | AppealCommitting |
| 11 | ValidatorsTimeout |
| 12 | LeaderTimeout |
| 13 | LeaderRevealing |
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_getTransactionStatus",
"params": [
{
"txId": "0x563f046c187d711127c51213ca62e2e4fee52009a98f0989a73a0a0382d21890"
}
],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"result": {
"status": "Finalized",
"statusCode": 7
},
"id": 1
}Notes:
- This endpoint reads the stored status directly. A deadline passing does not change this response until the corresponding protocol transition is materialized.
- Use this for polling — it's cheaper than
gen_getTransactionReceiptwhich returns the full receipt Finalizeis an action, not a status. Explorers and operator tooling that need projected state or recovery actions should usegen_getTransactionLifecycle.
gen_getTransactionLifecycle
Returns one advanced protocol lifecycle snapshot for a transaction. This endpoint is intended for explorers, operator tooling, and debugging; ordinary applications should prefer gen_getTransactionStatus or their SDK's simplified lifecycle model.
Method: gen_getTransactionLifecycle
Parameters:
request(object, required):txId(string, required): The transaction hash (hex-encoded with0xprefix)timestamp(integer, optional): Unix timestamp used for projection. Defaults to the node clock.
Returns: The stored state, its projection at evaluatedAt, and the resolution kernel's next action from one contract read.
Response Fields:
| Field | Type | Description |
|---|---|---|
storedStatus | string | Materialized on-chain status |
storedStatusCode | uint8 | Materialized status code |
projectedStatus | string | Status projected at evaluatedAt; projection does not write state |
projectedStatusCode | uint8 | Projected status code |
resolutionAction | string | Kernel action available from this snapshot, such as NoOp, ResolveAppeal, or Finalize |
resolutionActionCode | uint8 | Resolution action code |
resolutionSource | string | Resolution-plan source/reason |
resolutionSourceCode | uint8 | Resolution source code |
decisionId | string or null | Decimal decision identity used by decision-scoped protocol commands |
decisionActive | boolean | Whether decisionId still identifies the active decision |
executionGeneration | string or null | Current execution generation as a decimal string from the same snapshot; independent of retained decision provenance. Null when unavailable in a synthetic response. |
evaluatedAt | integer | Unix timestamp used for the projection |
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_getTransactionLifecycle",
"params": [
{
"txId": "0x563f046c187d711127c51213ca62e2e4fee52009a98f0989a73a0a0382d21890",
"timestamp": 1787868000
}
],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"result": {
"storedStatus": "Accepted",
"storedStatusCode": 5,
"projectedStatus": "Accepted",
"projectedStatusCode": 5,
"resolutionAction": "Finalize",
"resolutionActionCode": 6,
"resolutionSource": "FullReveal",
"resolutionSourceCode": 6,
"decisionId": "42",
"decisionActive": true,
"executionGeneration": "3",
"evaluatedAt": 1787868000
},
"id": 1
}Notes:
- Every response field comes from one lifecycle read at one pinned head and one timestamp.
storedStatusis the protocol state.projectedStatusdescribes what the kernel computes atevaluatedAt; it is not independently materialized.resolutionAction: "Finalize"is the authoritative finalization capability. There is deliberately noReadyToFinalizestatus and no redundantcanFinalizefield.- A client must not submit a decision-scoped command unless
decisionActiveis true anddecisionIdis non-null.
gen_syncing
Returns the GenVM sync status, indicating how far the node's local GenVM state is behind the chain tip.
Method: gen_syncing
Parameters: None
Returns: Object with sync status information
Response Fields:
| Field | Type | Description |
|---|---|---|
syncedBlock | string | Hex-encoded block number that GenVM has synced to |
latestBlock | string | Hex-encoded latest block number on the chain |
blocksBehind | number | Number of blocks GenVM is behind the chain tip (0 = fully synced) |
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_syncing",
"params": [],
"id": 1
}Example Response (Synced):
{
"jsonrpc": "2.0",
"result": {
"syncedBlock": "0x151ec5",
"latestBlock": "0x151ec5",
"blocksBehind": 0
},
"id": 1
}Example Response (Behind):
{
"jsonrpc": "2.0",
"result": {
"syncedBlock": "0x151e00",
"latestBlock": "0x151ec5",
"blocksBehind": 197
},
"id": 1
}Notes:
- This endpoint always returns an object (never
falselikeeth_syncing) - Use
blocksBehind == 0to determine if the node is fully synced - When the node is catching up,
gen_callrequests without an explicitblockNumberwill serve data at thesyncedBlockrather than waiting for full sync
cURL Example:
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_syncing",
"params": [],
"id": 1
}'gen_estimateGas
Reserved for estimating gas for a GenLayer transaction. Not implemented — the method is registered and reachable, but every call returns an error.
Method: gen_estimateGas
Parameters: None currently read — any params supplied are ignored because the method returns before parsing them
Returns: Nothing. The call always fails with JSON-RPC error code -32004
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_estimateGas",
"params": [],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"error": {
"code": -32004,
"message": "method not implemented: gen_estimateGas"
},
"id": 1
}Notes:
- The method is registered in the
genlayerendpoint group, so it is reachable and rate-limited like any other method — it just cannot succeed - The error code is
-32004("method not supported"), not-32601("method not found"): the node recognises the method and is declining to serve it, rather than failing to route it - Disabling the
genlayergroup, or this method specifically viaendpoints.methods, changes the response to the same-32004code with a "method not supported" message - Do not use gas estimation to size a transaction budget today; set fees explicitly instead
cURL Example:
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_estimateGas",
"params": [],
"id": 1
}'gen_getLogs
Queries GenLayer contract-emitted events, mirroring eth_getLogs semantics under the gen_ namespace. Events are produced by intelligent contracts (gl.chain.Event subclasses calling .emit()), persisted by the node while syncing accepted blocks, and indexed by contract address and topics.
Method: gen_getLogs
Parameters:
filter(object, required):fromBlock(string, optional): First block of the range (inclusive), as a hex (0x…), decimal string, or"latest". Block numbers are rollup (L2) block numbers — the same numberinggen_syncingreports. Defaults to the latest GenVM-synced block.toBlock(string, optional): Last block of the range (inclusive). Same format and default asfromBlock.address(string | string[], optional): Restrict to events emitted by this contract address (or any of these addresses). Empty matches all addresses.topics(array, optional): Positional topic filters, at most 4 entries. Each position isnull(wildcard), a 32-byte hex topic, or an array of topics (OR within the position). Positions matcheth_getLogssemantics: an event matches when every constrained position matches; an event with fewer topics than a constrained position does not match.blockHash(string, optional): 32-byte rollup block hash selecting exactly one block (EIP-234). Mutually exclusive withfromBlock/toBlock. The node resolves the hash through the rollup RPC; an unknown hash returns error-32001.status(string[], optional): Lifecycle stages to return, any of"accepted","finalized","cancelled". Filters on the emitting transaction's current stage, not on the stage it held atblockNumber. Defaults to["accepted", "finalized"]— every event whose emitting round still stands. An unknown value returns error-32602.
Returns:
An array of log objects, ordered by block number, then transaction, then per-transaction event index:
address(string): Address of the emitting contract.topics(string[]): The emitted topics, each a 32-byte hex string.topics[0]is the event signature topic:keccak256of the UTF-8 stringEventName(sortedIndexedFieldNames)— GenLayer hashes the sorted indexed field names without types, unlike Solidity's typed signature. Remaining topics are the indexed field values (calldata-encoded, right-padded to 32 bytes, or keccak-hashed when longer).data(string): Hex of the event payload. Deviation from eth: this is the GenVM calldata encoding of the event's keyword-argument map, not ABI encoding — decode it with a GenLayer SDK calldata codec.blockNumber(string): Rollup block number (hex) of the block whose sync recorded the event.transactionHash(string): 32-byte GenLayer transaction ID.logIndex(string): Deviation from eth: index of the event within its transaction (hex), not within the block.round(number): GenLayer extra — the consensus round of the executed result that emitted the event.status(string): GenLayer extra — the emitting transaction's current lifecycle stage:"accepted","finalized", or"cancelled". Not thegen_getTransactionStatusvocabulary: that method's"Canceled"means a transaction cancelled before execution, whereas a"cancelled"log belongs to a transaction whose acceptance an appeal overturned and which is re-executed on a later round.removed(boolean): Carries eth's meaning — the log is no longer part of the canonical set.trueexactly whenstatusis"cancelled".
blockHash and transactionIndex are omitted (not indexed node-side).
Semantics and limitations:
- GenLayer events follow their transaction's lifecycle: they become emitted when the transaction is ACCEPTED, become FINALIZED with it, and can be CANCELLED by a successful appeal. Events from successful, accepted executions are recorded at acceptance time (a reverted transaction emits no logs, matching EVM semantics).
- A log belongs to the block that emitted it.
blockNumberis always the acceptance block andfromBlock/toBlockselect on it, so a log's block is stable while itsstatusmoves. Each log is returned exactly once per query, whatever combination of statuses is requested — including when a transaction is accepted and finalized within the same block. statusis current, not as-of. The same query run twice can report a differentstatusfor a log (accepted → finalized) or stop returning it (accepted → cancelled), the same waygen_getTransactionStatusreports current state.- Default excludes cancelled logs. A query with no
statusreturns["accepted", "finalized"]. Events whose round was overturned on appeal are reachable withstatus: ["cancelled"], where they carryremoved: true. - Retention. Operators bound the store with
node.events.retention_blocks(default 1,000,000 blocks;0disables). Queries below the retention watermark return no logs. - Deviation from eth: an event emitted inside a write-enabled sandbox (
allow_write_ops=True) whose failure the contract catches still surfaces on the successful transaction — GenVM sandboxes are permission boundaries, not transactional ones, so caught-failure branch events are part of the transaction's log set (EVM drops reverted-sub-call logs). - Coverage requires the transaction to have executed on an executor that reports events in its result (all executors bundled with the currently pinned GenVM manager do); a future pin bundling a pre-2025-12 executor would not surface events for transactions it executes.
- Results reflect synced state: a transaction accepted moments ago becomes queryable once the sync loop writes its block (same trailing semantics as
gen_getContractStateatlatest).
Limits:
| Limit | Default | Config key |
|---|---|---|
| Block range per query | 10,000 blocks | node.rpc.getlogs_max_block_range |
| Logs per response | 10,000 entries | node.rpc.getlogs_max_results |
| Event retention | 1,000,000 blocks | node.events.retention_blocks |
Both are node-configurable (operators may reduce them); exceeding either returns error -32005 (query exceeds limits: … — distinct from HTTP rate limiting, which uses the same code). The method carries a default rate limit of 2 requests per second per IP, also configurable.
Errors:
-32602: malformed filter (invalid hex, more than 4 topic positions, non-32-byte topic,blockHashcombined with a block range,fromBlockgreater thantoBlock, unknownstatusvalue).-32005: block range or result count over the configured cap.-32001:blockHashunknown to the rollup.-32000: internal failure (details are logged, not returned).
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_getLogs",
"params": [
{
"fromBlock": "0x14e300",
"toBlock": "0x14e400",
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f",
"topics": [
"0x9f6dbb5f0f942152b8b254e69610e1c65e33a1bb61bd8f4bd88f0ec7bbe15c2f",
null
]
}
],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"address": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f",
"topics": [
"0x9f6dbb5f0f942152b8b254e69610e1c65e33a1bb61bd8f4bd88f0ec7bbe15c2f",
"0x2a00000000000000000000000000000000000000000000000000000000000000"
],
"data": "0x261674696d657374616d70014996d2c96e",
"blockNumber": "0x14e309",
"transactionHash": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49",
"logIndex": "0x0",
"round": 0,
"status": "finalized",
"removed": false
}
]
}gen_getTransactionData
Retrieves the on-chain transaction data for one round together with the contract execution outputs (returnData, leaderPublicData) recovered from the node's local trace store. If the node no longer holds the trace (restart, pruning), the on-chain fields are still returned with returnData: null — the transaction is never replayed.
Method: gen_getTransactionData
Parameters:
request(object, required):txId(string, required): The 32-byte transaction hash as a hexadecimal string with a0xprefix.round(uint8, optional): The consensus round whose execution outputs to return. Defaults to the latest round.
Returns:
An object with the on-chain transaction fields for the selected round plus the locally stored execution outputs:
id(string): The transaction ID as a hex string.sender(address): The address of the sender.recipient(address): The destination address of the transaction.activator(address): The address of the validator that activated the transaction.status(uint8): The current status of the transaction.statusName(string): The ABI transaction status name forstatus(Uninitialized,Pending,Proposing,Committing,Revealing,Accepted,Undetermined,Finalized,Canceled,AppealRevealing,AppealCommitting,ValidatorsTimeout,LeaderTimeout, orLeaderRevealing).result(uint8): The consensus result of the transaction (0Idle,1MajorityAgree,2MajorityDisagree,3MajorityTimeout,4DeterministicViolation,5NoMajority).resultName(string): The ABI result name forresult.txExecutionResult(uint8): The leader execution vote result (0NotVoted,1FinishedWithReturn,2FinishedWithError,3Timeout,4NondetDisagree,5DeterministicViolation).txExecutionResultName(string): The ABI vote type name fortxExecutionResult.txCallData(string): The transaction call data as a hex string.txExecutionHash(string): The execution hash of the transaction as a hex string.eqBlocksOutputs(string): The equivalent blocks outputs as a hex string.timestamps(object): Object containing various timestamps:Created(uint256): When the transaction was created.Pending(uint256): When the transaction became pending.Activated(uint256): When the transaction was activated.Proposed(uint256): When the transaction was proposed.Committed(uint256): When the transaction was committed.LastVote(uint256): When the last vote was cast.AppealSubmitted(uint256): When an appeal was submitted.LeaderRevealed(uint256): When the leader was revealed.
round(uint8): The round the execution outputs belong to (the requested round, or the latest round when omitted).returnData(string|null): The contract's return data as a0x-prefixed hex string, from the node's locally stored execution trace.nullwhen the node holds no trace for the round — clients must handlenullexplicitly.leaderPublicData(string): Leader data as a0x-prefixed hex string from the stored trace. Its encoding is defined by the GenVM specification.0xwhen no trace is available.
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_getTransactionData",
"params": [
{
"txId": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49"
}
],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49",
"sender": "0x5b70759760a85d92bf972b67ee8558e8e8da87d4",
"recipient": "0x73ca5a2b51edf506ceaf110a41780ec51294d89f",
"activator": "0xf27b07d36ff5b9dcd37fd3a09811d6456dd4483a",
"status": 7,
"statusName": "Finalized",
"result": 1,
"resultName": "MajorityAgree",
"txExecutionResult": 1,
"txExecutionResultName": "FinishedWithReturn",
"txCallData": "f5b31604617267730da4017570646174656420746573742073746f72616765066d6574686f64747570646174655f73746f7261676500",
"txExecutionHash": "0x76744dc491b1725eb2ba08cf516855c14aac310e36711368c04442fd39d61853",
"eqBlocksOutputs": "c0",
"timestamps": {
"Created": 1750774777,
"Pending": 1750774777,
"Activated": 1750774781,
"Proposed": 1750774786,
"Committed": 1750774791,
"LastVote": 1750774795,
"AppealSubmitted": 0,
"LeaderRevealed": 0
},
"round": 0,
"returnData": "0x00",
"leaderPublicData": "0x0e076e645f6f7574730d1b01aabb"
}
}Notes:
- Multi-round appeal data (
roundData) and fee accounting stay ongen_getTransactionReceipt; debug output (stdout, stderr, genvm_log) and trace replay stay ongen_dbg_traceTransaction. returnDataandleaderPublicDatacome from the node's local trace store only — a node that never executed the transaction (or pruned the trace) servesreturnData: nullandleaderPublicData: "0x"without replaying.- Retention.
returnDataandleaderPublicDatacome from the trace store, which operators bound withnode.trace.retention_blocks(default 1,000,000 blocks;0disables). Past the watermark this endpoint answers from chain data withreturnData: null, exactly as it does for a transaction the node never executed. - The trace is persisted asynchronously after a transaction is accepted, so a query issued immediately after acceptance can still return
returnData: null. Clients that need the execution output should poll until it appears (or fall back togen_dbg_traceTransaction, which replays on demand). - An explicit
roundbeyond the rounds the transaction has returns a-32001error. - A malformed
txId(not a 32-byte hex string) returns-32602; a transaction not found on-chain returns-32001. - This endpoint is in the
genlayergroup and is enabled by default.
cURL Example:
# Latest round
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_getTransactionData",
"params": [{
"txId": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49"
}],
"id": 1
}'
# Specific round
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_getTransactionData",
"params": [{
"txId": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49",
"round": 1
}],
"id": 1
}'gen_protocolVersion
Reserved for reporting the GenLayer protocol version. Not implemented — the method is registered and reachable, but every call returns an error.
Method: gen_protocolVersion
Parameters: None currently read — any params supplied are ignored because the method returns before parsing them
Returns: Nothing. The call always fails with JSON-RPC error code -32004
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_protocolVersion",
"params": [],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"error": {
"code": -32004,
"message": "method not implemented: gen_protocolVersion"
},
"id": 1
}Notes:
- The method is registered in the
genlayerendpoint group, so it is reachable and rate-limited like any other method — it just cannot succeed - The error code is
-32004("method not supported"), not-32601("method not found"): the node recognises the method and is declining to serve it, rather than failing to route it - To identify what a node is running, use the ops server's
GET /health, which reports node version and sync state eth_protocolVersionis unaffected — it is proxied to the GenLayer chain like any othereth_*method
cURL Example:
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_protocolVersion",
"params": [],
"id": 1
}'gen_subscribe
Opens a subscription to a stream of GenLayer consensus events over the node's inbound WebSocket endpoint. Notifications are pushed as they occur, removing the need to poll gen_getTransactionStatus.
Method: gen_subscribe
Transport: WebSocket only. This method is not available over HTTP — an HTTP POST cannot receive push notifications. See the WebSocket endpoint section for the URL.
Parameters:
stream(string, required): The event stream to subscribe to. One oftransactions,leadership,epochs,newHeads.
Returns: A subscription id (UUID v4 string) used to correlate notifications and to cancel via gen_unsubscribe
Streams:
| Stream | Event kinds delivered |
|---|---|
transactions | NewTransaction, TransactionActivated, TransactionReceiptProposed, VoteCommitted, VoteRevealed, TransactionAccepted, TransactionFinalized, TransactionUndetermined |
leadership | TransactionLeaderRotated, TransactionLeaderTimeout, TransactionLeaderChanged, TransactionLeaderRevealed, LeaderIdlenessProcessed, AppealStarted |
epochs | EpochAdvance, EpochFinalize, TimeoutsSet, SetEpochMinDuration |
newHeads | NewHeader |
Notification Fields:
| Field | Type | Description |
|---|---|---|
kind | string | The event name, matching the consensus contract's ABI event (e.g. TransactionAccepted) |
event | object | The decoded event payload. bytes32 values are 0x-prefixed hex; field names are camelCase |
event.raw | object | The originating chain log (block hash, transaction hash, topics), for reconciling against gen_getLogs |
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_subscribe",
"params": [
"transactions"
],
"id": 1
}Example Response:
{
"jsonrpc": "2.0",
"result": "b3f1c2de-9a44-4c1e-8f77-2b0a5d6e1c33",
"id": 1
}Example Notification:
{
"jsonrpc": "2.0",
"method": "gen_subscription",
"params": {
"subscription": "b3f1c2de-9a44-4c1e-8f77-2b0a5d6e1c33",
"result": {
"kind": "TransactionAccepted",
"event": {
"txId": "0x9c1f3a7b8d2e4f5061728394a5b6c7d8e9f0a1b2c3d4e5f60718293a4b5c6d7e",
"raw": {
"address": "0x1234567890abcdef1234567890abcdef12345678",
"blockNumber": "0x151ec5",
"transactionHash": "0x4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c"
}
}
}
}
}Notes:
- The subscription id is a UUID v4 generated by this node; it is never an upstream chain subscription id
- Streams are coarse by design: one subscription covers a whole lifecycle, so following a transaction end to end costs one subscription rather than one per event kind
- The
kindvalues are the consensus contracts' ABI event names, which are stable across node releases. They are deliberately not the node's internal topic names - Every payload is derived from on-chain logs and contains no node-private data. The node's own GenVM sync position is not exposed here; use
gen_syncing newHeadsis served for convenience and duplicateseth_subscribe("newHeads"), which is fed from the same upstream. Latency-sensitive clients should prefereth_subscribe- Notifications are live only — there is no historical replay or backfill. Reconcile any gap with
gen_getLogs - Server-side filtering (for example by
txId) is not supported; subscribe to the stream and filter client-side - Subscribing to an unknown stream returns error code
-32602with the list of supported streams - Exceeding
max_subs_per_connreturns error code-32000("subscription limit reached") - This method belongs to the
genlayerendpoint group; disabling that group disables it on the WebSocket transport too - Standard clients can consume this with their generic subscription API, since notifications use the conventional
gen_subscriptionenvelope. In go-ethereum:client.Subscribe(ctx, "gen", ch, "transactions")
wscat Example:
wscat -c ws://localhost:9151/ws
> {"jsonrpc":"2.0","method":"gen_subscribe","params":["transactions"],"id":1}
< {"jsonrpc":"2.0","result":"b3f1c2de-9a44-4c1e-8f77-2b0a5d6e1c33","id":1}
< {"jsonrpc":"2.0","method":"gen_subscription","params":{"subscription":"b3f1c2de-9a44-4c1e-8f77-2b0a5d6e1c33","result":{"kind":"NewTransaction","event":{"txId":"0x9c1f..."}}}}gen_unsubscribe
Cancels a subscription previously opened with gen_subscribe. The node stops sending notifications for that subscription and releases the resources backing it.
Method: gen_unsubscribe
Transport: WebSocket only, on the same connection that opened the subscription.
Parameters:
subscriptionId(string, required): The id returned bygen_subscribe
Returns: Boolean — true when a matching subscription was cancelled, false when the id is unknown on this connection
Example Request:
{
"jsonrpc": "2.0",
"method": "gen_unsubscribe",
"params": [
"b3f1c2de-9a44-4c1e-8f77-2b0a5d6e1c33"
],
"id": 2
}Example Response:
{
"jsonrpc": "2.0",
"result": true,
"id": 2
}Notes:
- An unknown subscription id is not an error: the response is
result: false, matching theeth_unsubscribeconvention - Subscriptions are scoped to the connection that created them. One connection cannot cancel another's subscription, and an id from a closed connection always returns
false - Explicit unsubscription is optional for cleanup: closing the WebSocket cancels every subscription on that connection automatically
- A notification already in flight when the cancellation is processed may still arrive; treat the
trueresponse as "no further events will be produced", not as a barrier - A missing or non-string parameter returns error code
-32602
wscat Example:
wscat -c ws://localhost:9151/ws
> {"jsonrpc":"2.0","method":"gen_unsubscribe","params":["b3f1c2de-9a44-4c1e-8f77-2b0a5d6e1c33"],"id":2}
< {"jsonrpc":"2.0","result":true,"id":2}Debug Methods
These methods are available for debugging and testing purposes during development.
Debug methods are off by default. To enable them, set genlayer_debug.enabled: true under node.rpc.endpoints in config.yaml. Enable them only on an RPC port that untrusted callers cannot reach:
node:
rpc:
endpoints:
genlayer_debug:
enabled: truegen_dbg_ping
Simple connectivity test method that returns a "pong" response.
Method: gen_dbg_ping
Parameters: None
Returns: "pong" string
Example:
{
"jsonrpc": "2.0",
"method": "gen_dbg_ping",
"params": [],
"id": 1
}Response:
{
"jsonrpc": "2.0",
"result": "pong",
"id": 1
}gen_dbg_trie
Get trie information for debugging node internal DB state.
Method: gen_dbg_trie
Parameters:
request(object, required): The trie debug requesttxID(string, required): Transaction ID (hex-encoded)round(integer, required): Round number
Returns: Trie debug information string
Example:
{
"jsonrpc": "2.0",
"method": "gen_dbg_trie",
"params": [
{
"txID": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6742d35Cc6634C0532925a3b8",
"round": 0
}
],
"id": 1
}Response:
{
"jsonrpc": "2.0",
"result": "graph TD\nnode0@{label: \"br seq=1\"}\nnode1@{label: \"ext seq=1 0x00000000000000000000000000000000000000000000000000000000000000000000000\"}\nnode1 --\u003e data2@{label: \"seq=1\n[21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]\"}\nnode0 --\u003e|0| node1\nnode3@{label: \"ext seq=1 0x72d46c3ada9f897c74d349bbfe0e450c798167c9f580f8daf85def57e96c3ea00000000\"}\nnode3 --\u003e data4@{label: \"seq=1\n[105 110 105 116 105 97 108 32 115 116 111 114 97 103 101 32 118 97 108 117 101 0 0 0 0 0 0 0 0 0 0 0]\"}\nnode0 --\u003e|3| node3\n",
"id": 1
}gen_dbg_traceTransaction
Retrieves the full execution trace for a transaction, including the return data, stdout/stderr, GenVM logs, and the exact error if execution failed.
Method: gen_dbg_traceTransaction
Parameters:
request(object, required):txID(string, required): Transaction ID (hex-encoded with0xprefix)round(integer, optional): Consensus round number (default: 0)
Returns: Transaction execution trace object
Response Fields:
| Field | Type | Description |
|---|---|---|
transaction_id | string | Transaction hash |
result_code | number | 0=success, 1=user error, 2=VM error, 3=internal error |
return_data | string | Hex-encoded contract return data |
stdout | string | Standard output captured during execution |
stderr | string | Standard error captured during execution |
genvm_log | array | Detailed GenVM execution log entries |
storage_proof | string | Hex-encoded storage proof hash |
run_time | string | Execution duration |
leader_public_data | string | Hex-encoded leader data; its encoding is defined by the GenVM specification |
stored_at | string (optional) | ISO 8601 timestamp of when the trace was stored; omitted when not available |
Example:
{
"jsonrpc": "2.0",
"method": "gen_dbg_traceTransaction",
"params": [{"txID": "0x563f046c187d711127c51213ca62e2e4fee52009a98f0989a73a0a0382d21890", "round": 0}],
"id": 1
}Response:
{
"jsonrpc": "2.0",
"result": {
"transaction_id": "0x563f046c187d711127c51213ca62e2e4fee52009a98f0989a73a0a0382d21890",
"result_code": 2,
"return_data": "0x2e0464617461b402696e76616c69645f636f6e7472616374...",
"stdout": "",
"stderr": "",
"genvm_log": [
{
"file": "src/lib.rs:82",
"level": "error",
"message": "vm error",
"error": {"causes": ["VMError(invalid_contract absent_runner_comment)"]}
}
],
"storage_proof": "0x99ff0d9125e1fc9531a11262e15aeb2c60509a078c4cc4c64cefdfb06ff68647",
"run_time": "0s",
"leader_public_data": "0x0e076e645f6f75747305"
},
"id": 1
}Notes:
- If the trace isn't stored locally (e.g., the node restarted or data was pruned), the endpoint will replay the transaction automatically.
- Retention. Operators bound the trace store with
node.trace.retention_blocks(default 1,000,000 blocks;0disables). A trace below the retention watermark is gone, so this endpoint falls back to a live replay — reconstructed from the latest on-chain view, and not byte-identical to the stored artifact. A single trace larger thannode.trace.max_trace_bytes(default 32 MB) is never stored, which produces the same fallback. - Field bound. The free-form execution-output fields (
return_data,stdout,stderr,genvm_log) carry whatever the replayed contract emitted, so their size is contract-controlled. Each is capped atnode.trace.max_debug_field_bytes(default 256 KB;0takes the default). When a field is cut, the truncation is marked in-band — a trailing...[truncated: N of M bytes omitted from gen_dbg trace]on a string field, or a final{"truncated": "..."}entry ongenvm_log— so the reply stays honest rather than silently short. - This endpoint is in the
genlayer_debuggroup, which is disabled by default. Enable it deliberately, and only on an RPC bind that untrusted callers cannot reach: it replays a transaction through GenVM and returns internal execution output, so it is debug tooling, not a public API. - Use
result_codeto determine execution outcome: 0 = contract returned successfully, 1 = contract raised a user error, 2 = GenVM encountered an error, 3 = internal error.
Ethereum Compatibility
The GenLayer Node also supports Ethereum-compatible methods that are proxied to the underlying infrastructure. These methods follow the standard Ethereum JSON-RPC specification (opens in a new tab) and are prefixed with eth_.
Examples of supported Ethereum methods:
eth_blockNumbereth_getBalanceeth_sendRawTransactioneth_call- And other standard Ethereum JSON-RPC methods
zkSync Compatibility
zkSync-compatible (opens in a new tab) methods are also supported and proxied to the underlying infrastructure. These methods are prefixed with zks_.
The default config.yaml turns the zksync group off (node.rpc.endpoints.zksync.enabled: false). Set it to true to forward zks_* calls.
Usage Examples
cURL
# Test connectivity (debug method, needs genlayer_debug enabled)
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_dbg_ping",
"params": [],
"id": 1
}'
# Execute a contract call
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_call",
"params": [{
"from": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6",
"to": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6",
"data": "0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d4c9db96c4b4d8b6",
"type": "read",
"status": "decided"
}],
"id": 1
}'
# Get contract schema
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_getContractSchema",
"params": [{
"code": "IyB7ICJEZXBlbmRzIjogInB5LWdlbmxheWVyOnRlc3QiIH0KCmZyb20gZ2VubGF5ZXIgaW1wb3J0ICoKCgojIGNvbnRyYWN0IGNsYXNzCmNsYXNzIFN0b3JhZ2UoZ2wuQ29udHJhY3QpOgogICAgc3RvcmFnZTogc3RyCgogICAgIyBjb25zdHJ1Y3RvcgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGluaXRpYWxfc3RvcmFnZTogc3RyKToKICAgICAgICBzZWxmLnN0b3JhZ2UgPSBpbml0aWFsX3N0b3JhZ2UKCiAgICAjIHJlYWQgbWV0aG9kcyBtdXN0IGJlIGFubm90YXRlZCB3aXRoIHZpZXcKICAgIEBnbC5wdWJsaWMudmlldwogICAgZGVmIGdldF9zdG9yYWdlKHNlbGYpIC0+IHN0cjoKICAgICAgICByZXR1cm4gc2VsZi5zdG9yYWdlCgogICAgIyB3cml0ZSBtZXRob2QKICAgIEBnbC5wdWJsaWMud3JpdGUKICAgIGRlZiB1cGRhdGVfc3RvcmFnZShzZWxmLCBuZXdfc3RvcmFnZTogc3RyKSAtPiBOb25lOgogICAgICAgIHNlbGYuc3RvcmFnZSA9IG5ld19zdG9yYWdlCg=="
}],
"id": 1
}'
# Get debug trie information (debug method, needs genlayer_debug enabled)
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_dbg_trie",
"params": [{
"txID": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6742d35Cc6634C0532925a3b8",
"round": 0
}],
"id": 1
}'
# Get transaction receipt
curl -X POST http://localhost:9151 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "gen_getTransactionReceipt",
"params": [{
"txId": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49"
}],
"id": 1
}'Ops Methods
These methods provide operational endpoints for monitoring the GenLayer node.
balance
Returns the configured operator's account balance in multiple formats including hexadecimal, wei, and GEN units.
Method: GET /balance
Parameters: None
Returns: JSON object containing the operator's balance in three formats
Example Request:
curl -X GET http://localhost:9153/balanceExample Response:
{
"balance_hex": "0x0de0b6b3a7640000",
"balance_wei": "1000000000000000000",
"balance_gen": "1.000"
}Response Fields:
balance_hex(string): The balance encoded as hexadecimal with "0x" prefixbalance_wei(string): The balance in wei as a decimal stringbalance_gen(string): The balance in GEN units formatted with 3 decimal places
Notes:
- This endpoint is only available when the node is running in validator mode
- The balance is always retrieved from the latest block
- The operator address is configured at node startup
- If the operator address is not configured, the zero address (0x0000...0000) is used
- Balance conversions: 1 GEN = 1,000,000,000,000,000,000 wei (10^18 wei)
- Returns HTTP 503 if the node is not in validator mode
- Returns HTTP 500 if there's an error retrieving the balance
Example Response (Zero Balance):
{
"balance_hex": "0x",
"balance_wei": "0",
"balance_gen": "0.000"
}Example Response (Large Balance):
{
"balance_hex": "0x3635c9adc5dea00000",
"balance_wei": "1000000000000000000000",
"balance_gen": "1000.000"
}Error Response (Not in Validator Mode):
{
"message": "Balance endpoint not available in full mode"
}cURL Example with Headers:
curl -X GET http://localhost:9153/balance \
-H "Accept: application/json" \
-vhealth
Returns the node's operational status and the typed condition catalog that explains it.
Method: GET /health
Parameters: None
Returns: JSON object containing the overall status, network/version information, validator identity (validator mode), and the full condition catalog
HTTP status: 503 when status is unavailable; 200 for every other state.
Example Request:
curl -X GET http://localhost:9153/healthExample Response:
{
"status": "healthy",
"network": "",
"node_version": "v0.6.0",
"protocol_version": "0.3.0",
"genvm_version": "v0.0.18",
"validator_address": "0x1234567890abcdef1234567890abcdef12345678",
"validator_moniker": "my-validator",
"conditions": {
"chain.rpc": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
"chain.websocket": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
"chain.sync": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:02:14Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
"consensus.ban": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
"consensus.stake": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
"consensus.operator_funds": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
"consensus.prime": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:04:41Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
"genvm.llm": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:31Z", "updated_at": "2026-09-01T10:29:56Z", "last_ok": "2026-09-01T10:29:56Z" },
"genvm.web": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:31Z", "updated_at": "2026-09-01T10:29:56Z", "last_ok": "2026-09-01T10:29:56Z" },
"node.disk": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
"node.snapshot_age": { "state": "healthy", "severity": "info", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" }
}
}Response Fields:
status(string): the most urgent state across the catalog —unknown,healthy,suspect,warning,degraded, orunavailablenetwork(string): The network the node is connected tonode_version,protocol_version,genvm_version(string): build versionsvalidator_address(string, validator mode only): The validator's addressvalidator_moniker(string, validator mode only): The validator's on-chain moniker; reported alongside the address even when emptyconditions(object): the full catalog. Every entry is always present — this is not a list of what is broken.state(string): current stateseverity(string): the catalog's fixed class —info,warning, orcritical. Not the state: acriticalcondition can behealthy.occurrences(number): accepted failed observations since process startconsecutive_failures(number): current failure streak; zero after recoverysince,updated_at,last_ok(string, optional): ISO 8601 lifecycle timestampsreason(string, present only when not healthy): stable, low-cardinality cause codeimpact(string, present only when not healthy): what the fault does to this nodeaction(string, present only when not healthy): what the operator should dodetails(object, present only when not healthy): typed fields explicitly allowlisted by the catalog entry. Raw provider payloads, prompts, credentials, URLs, and stack traces are rejected.
States
| State | Meaning | Alerted |
|---|---|---|
unknown | The node never observed this condition. | no |
healthy | The last observation succeeded. | no |
suspect | One failure, not yet confirmed. It may clear by itself. | no |
warning | The capability works, and it is confirmed sub-optimal. | yes |
degraded | The failure is confirmed. The capability does not work. | yes |
unavailable | The node cannot do its duty at all. | yes |
Roll-up precedence, most urgent first: unavailable, degraded, warning, suspect, unknown, healthy.
unknown is not on that ladder. It is only ever the state a condition starts in, so it means "not observed yet", never "faulty". A GenVM module rests there until a transaction needs one; node.snapshot_age rests there on a node that takes no snapshots. Letting either gate the endpoint would report unknown for a node where nothing is wrong.
If no condition has been observed at all, status is unknown — nothing observed is not the same as nothing wrong.
Only unavailable returns 503.
A condition can never exceed the liveness cap its catalog entry declares. The GenVM conditions are capped at degraded, so a dead LLM provider returns 200: that node is still booted, synced and voting — it votes Timeout on the transactions it cannot execute, which is the designed behaviour rather than an outage. Failing the endpoint would pull a working validator out of a load balancer.
consensus.ban, consensus.stake, chain.rpc, chain.websocket and chain.sync are capped at unavailable, because each of them stops the node doing its job.
Example: the LLM provider is dead — HTTP 200
{
"status": "degraded",
"conditions": {
"genvm.llm": {
"state": "degraded",
"severity": "critical",
"reason": "llm_request_failed",
"impact": "This validator cannot execute a transaction that needs an LLM. It votes Timeout, and the consensus contracts may penalize a Timeout vote.",
"action": "Check the affected LLM provider service and payment or billing status, restore access, or configure a working fallback. If credentials are changed through .env, restart the node.",
"occurrences": 2,
"consecutive_failures": 2,
"since": "2026-09-01T10:30:45Z",
"updated_at": "2026-09-01T10:31:12Z",
"last_ok": "2026-09-01T04:12:03Z"
}
}
}Example: the validator is banned — HTTP 503
{
"status": "unavailable",
"conditions": {
"consensus.ban": {
"state": "unavailable",
"severity": "critical",
"reason": "validator_banned",
"impact": "The consensus contracts do not select this validator for a round. The node earns nothing.",
"action": "Wait for the stated epoch. If the ban is permanent, restake with a new validator.",
"details": { "until_epoch": "4182", "permanent": "false" },
"occurrences": 1,
"consecutive_failures": 1,
"since": "2026-09-01T09:14:02Z",
"updated_at": "2026-09-01T10:31:12Z",
"last_ok": "2026-09-01T09:13:52Z"
}
}
}chain.sync — 503 only when the node stops syncing
Depth is not a fault. A node syncing from genesis is millions of blocks behind and perfectly healthy. What matters is whether the stored synced block advances.
| Reason | State | HTTP |
|---|---|---|
sync_lagging | warning | 200 |
sync_catching_up | degraded | 200 |
sync_stalled | unavailable | 503 |
sync_feed_frozen | unavailable | 503 |
The node never skips a duty because it is behind. It votes Timeout only on a transaction that reads state it has not synced; a new contract deploy still runs.
cURL Example with Headers:
curl -X GET http://localhost:9153/health \
-H "Accept: application/json" \
-vmetrics
Exposes Prometheus-compatible metrics for monitoring node performance and health.
Method: GET /metrics
Parameters: None
Returns: Prometheus-format text response with all collected metrics
Example Request:
curl -X GET http://localhost:9153/metricsExample Response:
# HELP genlayer_node_cpu_usage_percent Current CPU usage percentage (0-100)
# TYPE genlayer_node_cpu_usage_percent gauge
genlayer_node_cpu_usage_percent{component="node"} 15.5
genlayer_node_cpu_usage_percent{component="genvm-llm"} 45.2
genlayer_node_cpu_usage_percent{component="genvm-web"} 12.8
genlayer_node_cpu_usage_percent{component="webdriver"} 5.3
# HELP genlayer_node_memory_rss_bytes Resident Set Size (RSS) memory usage in bytes
# TYPE genlayer_node_memory_rss_bytes gauge
genlayer_node_memory_rss_bytes{component="node"} 524288000
genlayer_node_memory_rss_bytes{component="genvm-llm"} 2147483648
# HELP genlayer_node_network_rx_bytes_total Total bytes received across all network interfaces
# TYPE genlayer_node_network_rx_bytes_total counter
genlayer_node_network_rx_bytes_total{component="node"} 1073741824Available Metrics
The node collects metrics for three main components:
- Node - The main node process
- GenVM - The GenVM modules (LLM and Web)
- WebDriver - The WebDriver container
Operator Condition Metrics
genlayer_node_operator_condition_state{condition,state}: one-hot gauge for the current catalog state. All state series are prewarmed at process start.genlayer_node_operator_condition_occurrences_total{condition,reason}: accepted failed observations by stable reason.genlayer_node_operator_condition_transitions_total{condition,from,to}: lifecycle transitions, including recovery.genlayer_node_operator_condition_last_success_timestamp_seconds{condition}: Unix timestamp of the last successful observation, or zero before one occurs.
condition, state, reason, from, and to come from the fixed startup catalog; upstream error text is never used as a label.
occurrences_total counts differently per condition kind, so the counter stays comparable: an observed condition (folded from duty traffic) counts every failed observation, while a polled condition counts only entry into a failing state. Counting every failed poll would scale the counter with the poll interval rather than the fault — a three-day ban at a ten-second poll would read as roughly 26,000 events.
CPU Metrics
genlayer_node_cpu_usage_percent
Current CPU usage percentage (0-100).
Type: Gauge
Labels:
component: One ofnode,genvm-llm,genvm-web,webdriver
Example:
genlayer_node_cpu_usage_percent{component="node"} 15.5
genlayer_node_cpu_usage_percent{component="genvm-llm"} 45.2
genlayer_node_cpu_usage_percent{component="genvm-web"} 12.8
genlayer_node_cpu_usage_percent{component="webdriver"} 5.3Memory Metrics
genlayer_node_memory_rss_bytes
Resident Set Size (RSS) memory usage in bytes.
Type: Gauge
Labels:
component: One ofnode,genvm-llm,genvm-web,webdriver
Example:
genlayer_node_memory_rss_bytes{component="node"} 524288000
genlayer_node_memory_rss_bytes{component="genvm-llm"} 2147483648genlayer_node_memory_vms_bytes
Virtual Memory Size (VMS) in bytes.
Type: Gauge
Labels:
component: One ofnode,genvm-llm,genvm-web,webdriver
Note: Only available for node and GenVM components.
genlayer_node_memory_usage_bytes
Current memory usage in bytes (WebDriver only).
Type: Gauge
Labels:
component:webdriver
genlayer_node_memory_limit_bytes
Memory limit in bytes (WebDriver only).
Type: Gauge
Labels:
component:webdriver
genlayer_node_memory_percent
Memory usage percentage.
Type: Gauge
Labels:
component: One ofgenvm-llm,genvm-web,node,webdrivernode: Node address (hex format)
genlayer_node_memory_peak_bytes
Peak memory usage in bytes (GenVM components only).
Type: Gauge
Labels:
component: One ofgenvm-llm,genvm-webnode: Node address (hex format)
Network Metrics
genlayer_node_network_rx_bytes_total
Total bytes received across all network interfaces.
Type: Counter
Labels:
component: One ofnode,genvm-llm,genvm-web,webdrivernode: Node address (hex format)
Example:
genlayer_node_network_rx_bytes_total{component="node",node="0x84b6cbd007511352d3fea26834c5c39a440903c4"} 96879942
genlayer_node_network_rx_bytes_total{component="webdriver",node="0x84b6cbd007511352d3fea26834c5c39a440903c4"} 84genlayer_node_network_tx_bytes_total
Total bytes transmitted across all network interfaces.
Type: Counter
Labels:
component: One ofnode,genvm-llm,genvm-web,webdrivernode: Node address (hex format)
Disk Metrics
genlayer_node_disk_free_bytes
Available free disk space in bytes.
Type: Gauge
Labels:
component:nodedirectory: One ofgenlayer.db,keystore,logsnode: Node address (hex format)
genlayer_node_disk_total_bytes
Total disk space in bytes.
Type: Gauge
Labels:
component:nodedirectory: One ofgenlayer.db,keystore,logsnode: Node address (hex format)
genlayer_node_disk_usage_bytes
Used disk space in bytes.
Type: Gauge
Labels:
component:nodedirectory: One ofgenlayer.db,keystore,logsnode: Node address (hex format)
genlayer_node_disk_usage_percent
Used disk space as percentage.
Type: Gauge
Labels:
component:nodedirectory: One ofgenlayer.db,keystore,logsnode: Node address (hex format)
Node Status Metrics
genlayer_node_blocks_behind
Number of blocks behind the latest block.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_latest_block
Latest block number in the network.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_processing_block
Currently processing block number.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_synced_block
Last synced block number.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_synced
Node synchronization status (1 = synced, 0 = not synced).
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_prime_blocked_insufficient_funds
Whether a mandatory validator prime is currently blocked by an unfunded operator wallet (1 = blocked, 0 = not blocked). While blocked the validator cannot pay for consensus duty transactions and is flagged idle each round — fund the operator wallet to recover.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_uptime_seconds
Node uptime in seconds.
Type: Gauge
Labels:
component:nodenode: Node address (hex format)
Transaction Metrics
genlayer_node_transactions_accepted_synced_total
Total number of accepted and synced transactions.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_transactions_activated_total
Total number of activated transactions.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_transactions_leader_proposed_total
Total number of transactions proposed as leader.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_transactions_leader_revealed_total
Total number of transactions revealed as leader.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_transactions_validator_commit_total
Total number of validator commit votes.
Type: Gauge
Labels:
node: Node address (hex format)
genlayer_node_transactions_validator_reveal_total
Total number of validator reveal votes.
Type: Gauge
Labels:
node: Node address (hex format)
Gas Metrics (duty transactions)
Emitted inline from the shared consensus gas estimator and transactor, so they cover every duty method (finalizeTransaction, commitVote, revealVote, proposeReceiptWithFees, topUpAndSubmitAppeal, activateTransaction, validatorPrime, epochAdvance, ...). All are true counters.
genlayer_node_gas_estimation_total
Outcome of every duty gas estimation.
Type: Counter
Labels:
method: Contract method nameoutcome: One ofsuccess(real RPC estimate),fallback_block_limit(estimation failed; submitted at full block gas limit),fallback_cache/fallback_cache_scaled(estimation failed; reused gas from the shared calldata-length-keyed cache, possibly learned from a different method)
genlayer_node_gas_estimation_capped_total
Estimation succeeded but the safety-multiplied estimate (or per-call reserve) exceeded the block gas limit and was silently capped — the safety margin is gone while everything still works.
Type: Counter
Labels:
method: Contract method name
genlayer_node_gas_shortfall_total
Confirmed on-chain out-of-gas: the transaction landed but ran out of gas mid-execution. Self-heals — the node retries with a larger gas limit — so a single occurrence is not actionable; a sustained rate signals systematic under-estimation (treat as a warning, not a page).
Type: Counter
Labels:
method: Contract method namereason: One oftyped_shortfall(InsufficientGasForInternalMessageCreationrevert with exact gas requirement),out_of_gas(explicitOutOfGasrevert),heuristic(undecoded revert that consumed gas above the exhaustion threshold)
genlayer_node_gas_block_limit_exhausted_total
Gas shortfall while the gas limit was already capped at the block limit: the work cannot fit in one block and retrying cannot help. Non-self-healing — any occurrence is actionable (page-worthy).
Type: Counter
Labels:
method: Contract method name
genlayer_node_gas_spent_wei_total
Cumulative wei actually billed for gas, from each mined duty receipt (gasUsed × effectiveGasPrice). Reverted attempts are billed too and are accounted per attempt; submissions that never land cost nothing. The reverted slice is the exact cost of failed work, and sum(genlayer_node_gas_spent_wei_total) is the operator's total billed duty gas.
Type: Counter
Labels:
method: Contract method nameoutcome: One ofsuccess,revertedprice_source:effective(receipt's billed price) orfee_cap(submitted fee cap used as an upper bound when the RPC omitseffectiveGasPrice)
Consensus Submission Metrics
genlayer_node_consensus_submit_total
Terminal outcome of every duty submission.
Type: Counter
Labels:
method: Contract method nameoutcome: One ofsuccess(landed and executed),reverted(landed, contract rejected — gas still billed),failed(never landed: RPC/nonce/funds/authorization)
genlayer_node_consensus_revert_total
Decoded revert causes, one increment per observed revert (attempt-level).
Type: Counter
Labels:
method: Contract method nameerror: Decoded custom error from the fee/gas allowlist (InsufficientFeesForRound,InsufficientFees,FailedFeeTransfer,FeeSettlementNotCompleted,MaxPriceExceeded,ExecutionBudgetExceeded,MessageBudgetExceeded,BudgetTooLow,RollupBudgetBelowFloor,InsufficientBondCustody,InvalidAppealBond,AppealBondTooLow,InsufficientGasForInternalMessageCreation,OutOfGas); anything else collapses toother
Fee Flow Metrics (v0.6 ConsensusMainWithFees)
Note: there are no dedicated appeal-bond metrics. No node runtime path submits appeals — appellants are external clients funding bonds from their own wallets. If a validator appeal duty ever ships, the method-labeled gas/submission metrics above cover it under method="topUpAndSubmitAppeal" with no code change.
genlayer_node_fees_operator_insufficient_funds_total
Duty submissions rejected because the operator account cannot cover value + gas. Complements the genlayer_node_prime_blocked_insufficient_funds gauge by leaving a trace of dips that recover between scrapes. A sustained increase means the operator wallet needs funding (page-worthy).
Type: Counter
genlayer_node_fees_settlement_events_total
Fee-settlement events decoded from this node's own mined finalize receipts. Finalize runs under a staggered lease, so each finalize is observed by exactly one node — aggregate fleet-wide with sum by (network). Use the severity label to alert on failures without enumerating event names: genlayer_node_fees_settlement_events_total{severity="alarm"} covers all three failure modes and any future one.
Type: Counter
Labels:
event: One ofUnifiedFeesDistributed(fees distributed at settlement),FeesRefunded(surplus returned to the sender),FeeSettlementFailed(finalize succeeded but settlement failed — validators unpaid untilretryFeeSettlement),CommitFinalFeesFailed(tx finalized without fee settlement),ValueWithdrawalFailed(a best-effort transfer — refund/appealer payout — failed; funds stay custodied pending recovery)severity:alarmfor the three failure events (unpaid or stuck-funds modes needing operator action) orinfofor the normalUnifiedFeesDistributed/FeesRefundedhappy path
genlayer_node_fees_distributed_wei_total
Total wei moved at fee settlement across all recipients — committee payouts plus sender/contributor refunds plus appealer rewards — summed from UnifiedFeesDistributed amounts in this node's finalize receipts. It excludes amounts retained in staking custody (amountToStaking). This is a network "value settled" liveness signal, not per-node earnings — for a validator's own income use genlayer_node_fees_earned_wei_total.
Type: Counter
genlayer_node_fees_earned_wei_total
This node's validator earnings credited at prime, read from the ValidatorPrime event when a prime is effective. This is the accurate per-node income (unlike fees_distributed_wei_total, which is whole-committee plus refunds). Earnings settle into stake per epoch, so this series lags settlement by up to one prime interval. Pair type="fee_rewards" with genlayer_node_gas_spent_wei_total for operator net P&L (earned − gas_spent).
Type: Counter
Labels:
type: One offee_rewards(fee-flow income),validator_rewards,delegator_rewards(staking-reward components)
genlayer_node_fees_penalties_wei_total
This node's fee penalties deducted at prime, read from the same ValidatorPrime event. Kept separate from fees_earned_wei_total so earnings stay gross income; subtract this for net.
Type: Counter
genlayer_node_external_message_delivery_total
External messages this node's own delivery calls (flushExternalMessages, flushInternalMessages, finalizeTransaction) resolved or paused, read from their mined receipts. A paused message was not called and not charged; the node retries it with a backoff (30 s doubling to 10 min) until the chain reports it issued. A paused_* series that keeps rising for one network means a message nobody can deliver.
Type: Counter
Labels:
outcome:delivered(ran and succeeded),call_failed(ran and failed; final),paused_gas(no room to forward its gas envelope),paused_value(its value was not yet available),cancelled_unfunded(its open-bucket envelope was never funded; value refunded),cancelled_ceiling(its gas limit is above the current ceiling; value refunded)
genlayer_node_external_message_reimbursed_wei_total
Wei the chain reimbursed this node for running external messages: gas used times the transaction's locked gas price. Pair it with gas_spent_wei_total for the net cost of delivery.
Type: Counter
Go Runtime Metrics
The endpoint also exposes standard Go runtime metrics including:
go_gc_duration_seconds- Garbage collection duration summarygo_goroutines- Number of active goroutinesgo_memstats_*- Go memory statistics (heap, stack, GC)go_threads- Number of OS threads
Process Metrics
Standard process metrics are also available:
process_cpu_seconds_total- Total CPU timeprocess_resident_memory_bytes- Resident memory sizeprocess_virtual_memory_bytes- Virtual memory sizeprocess_open_fds- Number of open file descriptorsprocess_network_receive_bytes_total- Network bytes receivedprocess_network_transmit_bytes_total- Network bytes transmitted
Prometheus Handler Metrics
Metrics about the metrics endpoint itself:
promhttp_metric_handler_requests_total- Total scrape requests by status codepromhttp_metric_handler_requests_in_flight- Current scrapes being served
Configuration
Metrics collection can be configured in the node configuration file:
metrics:
interval: "15s" # Default interval for all collectors
collectors:
node:
enabled: true # Enable/disable node metrics
interval: "30s" # Optional: Override default interval
genvm:
enabled: true # Enable/disable GenVM metrics
# Uses default interval (15s)
webdriver:
enabled: true # Enable/disable WebDriver metrics
interval: "60s" # Optional: Override default intervalDefault Values
metrics.interval: 15s (applies to all collectors by default)enabled: true (for each collector)interval: Uses metrics.interval if not specified per collector
Configuration Priority
- Collector-specific
interval(if specified) - Global
metrics.interval(if specified) - System default (15s)
Troubleshooting
No Metrics for GenVM
GenVM processes are ephemeral and only run when executing smart contracts. If you don't see GenVM metrics, it's likely that no contracts are currently being executed.
WebDriver Metrics Missing
Ensure the WebDriver container is running:
docker ps | grep genlayer-node-webdriverIf not running, start it:
task docker:webdriver:docker-compose:up:detachMetrics Not Updating
Check if metrics collection is enabled in your configuration and that the node has been restarted after configuration changes.
Performance Considerations
- Collection intervals can be increased to reduce overhead further
- Each collector runs independently and won't block others if one fails
snapshot
Produces a consistent snapshot of the node's state at the current block. Two modes selected by the request body: stream the snapshot back as a gzipped tar response (operator path), or write it to a destination directory on the node's filesystem (zero-downtime upgrade-controller path).
Method: POST /snapshot
Authentication: required. Send the operator token in either header:
Authorization: Bearer <token>
X-Admin-Auth: <token>The token is the value of GENLAYERNODE_NODE_OPERATOR_TOKEN on the node process. When that variable is unset, the endpoint returns 401 to every caller regardless of header value (default-deny). The other ops routes (/metrics, /health, /balance) are unaffected and remain public.
Network exposure: the ops server is intended for in-host or in-cluster access (Prometheus scraping /metrics, k8s probes hitting /health). The endpoint may not be reachable from outside the host depending on the deployment's firewall. The recommended operator workflow is therefore one of:
- SSH onto the node and call the endpoint via
127.0.0.1:9153, or - Open an SSH tunnel from the operator workstation to the node (
ssh -L 9153:127.0.0.1:9153 <node>) and calllocalhost:9153through it.
Parameters:
-
scope(string, optional): Which stores the archive carries —all(default),genvmorops. An absent scope meansall, so a caller written before the store split keeps its behaviour. An unrecognised value is rejected with400.Scope Contents Portable between nodes allboth stores, operation store first No genvmchain-derived history only Yes opsthis node's consensus record only No Only
genvmmay be given to another operator.allandopscarry the vote salts this node generated and the nonce ownership of its operator key; restoring either onto a different node would make that node act on votes it never cast and nonce slots it never signed. The node refuses to start against an operation store stamped with another operator, but that guard runs at startup, not at copy time — treat a non-portable archive accordingly.Every archive carries a
snapshot.jsonmanifest recording the format version, the scopes inside, the rollup block the GenVM store was captured at, and — forallandops— the operator address that produced it. -
destDir(string, optional): Absolute path on the node's filesystem, which must resolve to a location inside the configured snapshot directory (node.admin.snapshot_dir, default<datadir>/snapshots). When set, switches to write-to-path mode; when omitted or empty, the snapshot is streamed back asapplication/gzip. Paths outside the snapshot directory are rejected with400.
Returns:
- Stream mode (
destDiromitted): gzipped tar archive in the response body,Content-Type: application/gzip. - Write-to-path mode (
destDirset): empty body,204 No Content.
Example Request — Stream Mode (operator):
On the node (after ssh <node>):
curl -X POST http://127.0.0.1:9153/snapshot \
-H "X-Admin-Auth: $GENLAYERNODE_NODE_OPERATOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{}' \
-o snapshot.tar.gzOr via SSH tunnel from a workstation (forwards 9153 to the node's loopback):
ssh -L 9153:127.0.0.1:9153 -N <node> &
curl -X POST http://localhost:9153/snapshot \
-H "X-Admin-Auth: $GENLAYERNODE_NODE_OPERATOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{}' \
-o snapshot.tar.gzThe response body is a stream — curl can write it to a file (as above) or pipe it through any tool that reads stdin, without staging on local disk.
Example Response — Stream Mode:
HTTP/1.1 200 OK
Content-Type: application/gzip
Content-Disposition: attachment; filename="snapshot.tar.gz"
<gzipped tar archive of the snapshot's files>Example Request — Write-to-Path Mode (upgrade controller):
curl -X POST http://127.0.0.1:9153/snapshot \
-H "X-Admin-Auth: $GENLAYERNODE_NODE_OPERATOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"destDir": "/var/lib/genlayer/data/node/snapshots/bradbury-100"}'
Take only the portable half, to hand to another operator:
```bash
curl -sS -X POST http://localhost:9153/snapshot \
-H "Authorization: Bearer $OPERATOR_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"scope": "genvm"}' -o genvm-snapshot.tar.gz
**Example Response — Write-to-Path Mode:**
HTTP/1.1 204 No Content
**HTTP Status Codes:**
- `200 OK`: Stream mode succeeded; body carries the gzipped tar archive.
- `204 No Content`: Write-to-path mode succeeded; snapshot is on disk at `destDir`.
- `400 Bad Request`: `destDir` is not absolute, resolves outside the configured snapshot directory, or already exists.
- `401 Unauthorized`: Missing, invalid, or empty operator token (also returned when `GENLAYERNODE_NODE_OPERATOR_TOKEN` is unset on the node).
- `500 Internal Server Error`: Snapshot creation failed (e.g., disk full, DB checkpoint error).
**Notes:**
- A snapshot may be several GB. In stream mode the connection stays open for the duration of the snapshot — configure reverse proxies / load balancers with adequate read timeouts and disable response buffering.
- In stream mode, a dropped connection leaves no on-disk artifact on the node; retry the request from scratch.
- In write-to-path mode, `destDir` must be an absolute path that does not yet exist and that resolves (after `filepath.Clean`) to a location inside the configured snapshot directory; the node creates it. Paths that escape the directory via `..` or point elsewhere on the filesystem are rejected with `400`. This bounds where an operator-token holder can write a checkpoint — still treat the operator token as a credential that can write under the node user's filesystem permissions within that directory.
- **Snapshot directory** (`node.admin.snapshot_dir`): bounds write-to-path mode. A relative value resolves under `datadir` (so the default `snapshots` is `<datadir>/snapshots`); an absolute value is used as-is, letting a node target a dedicated disk. The node creates the directory with `0700` permissions on first use if it does not exist.
- **Docker deploys**: in write-to-path mode, `destDir` (and the snapshot directory) is resolved inside the node container's filesystem, not on the host. The default `<datadir>/snapshots` falls under whatever volume the deployment mounts for `datadir`, so it is host-reachable wherever that volume maps. To use a different location, set `snapshot_dir` to a path that is itself volume-mapped identically on both sides, or stick with stream mode. Stream mode is unaffected — the response body is sent over HTTP regardless of where the node runs.
- The snapshot includes the full chain database (SSTables, MANIFEST, WAL) at the moment of capture. Treat the artifact as sensitive — anyone holding it can reconstruct the node's state.
- Token comparison is constant-time and length-padded so neither the value nor the length of the secret can be inferred from response timing.
- Rotation: update `GENLAYERNODE_NODE_OPERATOR_TOKEN` and restart the node. Tokens are read once at startup and not hot-reloaded.
**Forward-compatibility note:** when the zero-downtime upgrade controller (Phase 3) lands, `/snapshot` will additionally accept a second role (`upgrade`) so the controller can drive the snapshot + stop + start sequence with its own credential. The operator-token path documented here remains the same — no migration is required for operator tooling.
**cURL Example with Bearer header:**
```bash
curl -X POST http://127.0.0.1:9153/snapshot \
-H "Authorization: Bearer $GENLAYERNODE_NODE_OPERATOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{}' \
-o snapshot.tar.gz