From 16ce148ef1466d7eae7f17d2d163f0faa9eb3945 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:07:33 +0000 Subject: [PATCH 01/43] docs: Adds new opt-in config fields to enable estimated read/write metrics for FlatKV state-commit, state-store, and receipt-store Pebble DBs, plus exposes FlatKV config fields in the server config template. (sei-protocol/sei-chain#3566) --- node/advanced-config-monitoring.mdx | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index d9c80c2..c46da6d 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -582,6 +582,38 @@ FlatKV metrics carry these labels where applicable: A single FlatKV-level knob controls Pebble's internal (per-DB) metrics: `enable-pebble-metrics` under `[state-commit.flatkv]` in `app.toml` (default `true`). The node honors this key when it is present, but the `app.toml` that `seid init` generates does not include it. To change the default, add the key manually. The value propagates to every data DB (account, code, storage, legacy, and metadata) during initialization. It overrides any per-DB `EnableMetrics` settings. Configure Pebble metrics through this knob, not through the individual per-DB settings. + +## Pebble estimated read/write metrics + +SeiDB can emit lightweight, opt-in estimates of logical read and write operations observed by its Pebble-backed wrappers. These counters are disabled by default and are intended to approximate read amplification (estimated reads divided by estimated writes) without the overhead of Pebble's full internal metrics. + +### Available metrics + +| Metric | Type | Unit | Description | +| --- | --- | --- | --- | +| `pebble_estimated_reads` | Counter | `{count}` | Estimated logical PebbleDB reads observed by SeiDB wrappers. | +| `pebble_estimated_writes` | Counter | `{count}` | Estimated logical PebbleDB writes observed by SeiDB wrappers. | + +Both counters carry a `db` attribute identifying the database the measurement applies to (derived from the base name of the data directory), so you can distinguish FlatKV data DBs, the state-store backend, and receipt storage. + +### Enabling the counters + +These estimates are controlled per subsystem in `app.toml`. All default to `false`: + +| Config key | Section | Description | +| --- | --- | --- | +| `enable-read-write-metrics` | `[state-commit.flatkv]` | Emits estimated read/write counters for FlatKV's Pebble DBs. | +| `ss-enable-read-write-metrics` | `[state-store]` | Emits estimated PebbleDB MVCC read/write counters for the state-store backend. Applies when `ss-backend = "pebbledb"`. | +| `enable-read-write-metrics` | `[receipt-store]` | Emits estimated read/write counters for Pebble-backed receipt storage. | + +For example, to enable the FlatKV counters, add the following to `app.toml`: + +```toml +[state-commit.flatkv] +enable-read-write-metrics = true +``` + + ## LittDB OpenTelemetry metrics LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` instead of a private Prometheus client. When `MetricsEnabled` is set, LittDB configures a Prometheus exporter on the global provider and serves `/metrics` on `MetricsPort` (default `9101`). The `MetricsNamespace` and `MetricsRegistry` config fields were removed, and all metric names use a fixed `litt_` prefix. From 81b7e66e3a02cd5c77df3e247a6efea32d6ec08d Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:08:27 +0000 Subject: [PATCH 02/43] docs: Autobahn (GigaRouter) now supports fullnode mode driven by the node's `mode` config field, adds a new `max_inbound_fullnode_peers` config field, moves the `autobahn-config-file` key to top-level in config.toml, and changes the `evmrpc` field in autobahn.json to be required on every validator. (sei-protocol/sei-chain#3525) --- evm/reference.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/evm/reference.mdx b/evm/reference.mdx index 1fb73ef..f629d44 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -127,7 +127,11 @@ You can also browse every method below in the interactive explorer above. This s **Sei-specific behavior:** Decodes the bytes to an ethtypes.Transaction, wraps it in a Cosmos MsgEVMTransaction, and broadcasts it through CometBFT. By default, it uses a CheckTx-level BroadcastTx (that is, `broadcast_tx_sync`), and slow mode uses BroadcastTxCommit. Non-zero CheckTx codes are returned as ABCI errors, not geth mempool errors. Legacy (non-1559) transactions must set gasPrice at or above the governance minimum (currently 50 gwei on Sei Mainnet). Blob (EIP-4844) transactions are not enabled. The method supports per-sender EvmProxy forwarding. -**Under Autobahn (`AutobahnConfigFile` set):** Transaction broadcast routes through Autobahn's producer-backed mempool instead of CometBFT's `TxMempool`. This mempool admits EVM transactions strictly in sequential nonce order for each sender. It rejects a transaction with a bad-nonce error if its nonce does not match the sender's next expected nonce. Senders must therefore submit nonces contiguously. +**Under Autobahn (`AutobahnConfigFile` set):** The node's role follows the top-level `mode` field in the CometBFT config. With `mode = "validator"`, the node runs the validator path and backs transaction submission with a local mempool; any other mode runs the node as a fullnode that loads the committee for routing only and has no local mempool. + +On a validator, transaction broadcast routes through Autobahn's producer-backed mempool instead of CometBFT's `TxMempool`. This mempool admits EVM transactions strictly in sequential nonce order for each sender. It rejects a transaction with a bad-nonce error if its nonce does not match the sender's next expected nonce. Senders must therefore submit nonces contiguously. + +On an Autobahn fullnode, there is no local mempool. `eth_sendRawTransaction` is proxied to the shard owner (the committee member that owns the sender's EVM shard), which must have an EVM RPC URL configured — the `evmrpc` field is required for every committee member in the Autobahn config file (`autobahn.json`). The underlying Tendermint `broadcast_tx_*` methods (`broadcast_tx_async`, `broadcast_tx_sync`, `broadcast_tx_commit`) have no local backing on a fullnode and return the error `autobahn fullnode has no local mempool; broadcast_tx_* must be sent to a validator`. Submit transactions to a validator (or let `eth_sendRawTransaction`'s per-sender proxy forwarding handle the routing). Pending-nonce queries also behave differently: `eth_getTransactionCount` with the `pending` tag returns the on-chain confirmed nonce (the pending nonce lives on the shard owner), and `unconfirmed_txs` / `num_unconfirmed_txs` return empty. Under Autobahn, the default CheckTx-synchronous broadcast (`broadcast_tx_sync`, the same path named above) blocks while the mempool is full. It returns only after capacity is available. As a result, a default `eth_sendRawTransaction` call can stall for as long as the mempool stays full. The async path (`broadcast_tx_async`) may silently drop the transaction instead. The `unsafe_flush_mempool` Tendermint RPC endpoint is not supported under Autobahn and returns `unsafe_flush_mempool is not supported with autobahn mempool`. From 35822b4b475e0df3c58db6b1a57e6fb4b685a74a Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:08:57 +0000 Subject: [PATCH 03/43] docs: A new config field `evm.max_estimate_gas_calls` limits the number of calls allowed in an `eth_estimateGasAfterCalls` request, defaulting to 100. (sei-protocol/sei-chain#3631) --- evm/reference.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/reference.mdx b/evm/reference.mdx index f629d44..97b90b6 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -1086,7 +1086,7 @@ The `filter` object applies only to `logs` subscriptions. For `newHeads`, pass t **Limited.** Estimates gas for a transaction after it applies a sequence of preceding calls to the same simulated state. -**Sei-specific behavior:** This is a non-standard Sei/geth extension (not part of the standard Ethereum JSON-RPC spec). It has the same gas-cap and fail-fast-limiter behavior as `eth_estimateGas`. +**Sei-specific behavior:** This is a non-standard Sei/geth extension (not part of the standard Ethereum JSON-RPC spec). It has the same gas-cap and fail-fast-limiter behavior as `eth_estimateGas`. The number of preceding `calls` is capped by the `evm.max_estimate_gas_calls` config (default 100). A request whose `calls` array exceeds this limit is rejected early with a 'too many calls' error before any state wrapping or resource acquisition. **Parameters:** From 3fbe09db2cad3a84a5d0ca0f210fea0ed4703c40 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:10:19 +0000 Subject: [PATCH 04/43] docs: Adds a new 'auto' value for the sc-write-mode node config field, enabling runtime write-mode migration transitions without restarts, with important operator warnings about never using it on flatkv_only nodes. (sei-protocol/sei-chain#3581) --- node/giga-storage-migration.mdx | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/node/giga-storage-migration.mdx b/node/giga-storage-migration.mdx index 3ee7f6a..76ce52f 100644 --- a/node/giga-storage-migration.mdx +++ b/node/giga-storage-migration.mdx @@ -258,6 +258,57 @@ While a node is in a migration mode: a migration. - The migration boundary advances at most once per block. + + +### The `auto` write mode (runtime transitions without restarts) + +`auto` is an additional `sc-write-mode` value that removes the stop-edit-restart +step between each migration stage. Instead of fixing the write mode in `app.toml`, +`auto` derives the **effective** write mode from the migration metadata persisted +in FlatKV. A node stays on `auto` in config across the entire migration chain, +and the effective mode advances through `memiavl_only` → `migrate_evm` → +`evm_migrated` → `migrate_all_but_bank` → `all_migrated_but_bank` → +`migrate_bank` → `flatkv_only` via coordinated runtime transitions, with no +restart required at any stage. + +```toml copy +[state-commit] +sc-write-mode = "auto" +``` + +Under `auto`: + +- FlatKV is **lazy**. On a fresh node the FlatKV directory is not created on disk + until the first `migrate_evm` transition occurs. While the directory is absent, + the store is effectively `memiavl_only` and FlatKV does not contribute to the + app hash at all. +- Transitions walk the migration chain **one step forward at a time**, and a + migration stage must have completed its drain before the node can advance to + the following steady state. Skipping steps, moving backward, or exiting a + migration mid-flight is rejected. +- Transitions are **deterministic and level-triggered** across the quorum: every + node performs the same transition at the same height. Because migration writes + feed the app hash, a transition that is not yet persisted is re-evaluated on + every commit, so a node that restarts inside the pre-commit window re-fires the + transition and self-heals rather than diverging. +- On restart, the effective mode is re-derived from the on-disk migration state. + An in-flight migration resumes in its migration mode; a completed one comes up + in the following steady state even if no explicit completion flip was issued. + +State proofs remain supported only for memiavl-resident data — FlatKV has no +proof builder. This is not specific to `auto`, but it becomes operator-visible +once an `auto` chain migrates stores into FlatKV. + +**Never set `sc-write-mode = "auto"` on an existing `flatkv_only` +node.** `auto` is only valid for a node whose history began in `memiavl` (for +example, a node switching from `memiavl_only`). A node that started in FlatKV +mode must keep `flatkv_only` forever. Depending on the node's on-disk metadata, +setting `auto` on a flatkv-origin node fails in one of two ways: with migration +metadata present (for example a state-synced node), every commit fails with a +version-mismatch error; with metadata absent (a genesis FlatKV chain), mode +derivation resolves `memiavl_only` and reads silently route to an empty +memiavl, serving incorrect empty results. + ### Operator-facing knobs **`sc-keys-to-migrate-per-block`** (`app.toml`, `[state-commit]` section) From 25ebb8c02f3623002643e0279661de28bae6e1bc Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:10:48 +0000 Subject: [PATCH 05/43] docs: Adds a new EVM RPC config field `evm.max_subscriptions_logs` to cap concurrent logs subscriptions (default 1000), beyond which new logs subscriptions are rejected. (sei-protocol/sei-chain#3621) --- evm/reference.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/reference.mdx b/evm/reference.mdx index 97b90b6..70f09b2 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -963,7 +963,7 @@ Heights with pruned or partial base-fee data are skipped in `baseFeePerGas`, but **Limited.** Opens a WebSocket-only push subscription for newHeads or logs notifications. -**Sei-specific behavior:** Available over WebSocket only. SubscriptionAPI is not registered on the HTTP server, so over HTTP the method returns rpc.ErrNotificationsUnsupported. Only 'newHeads' and 'logs' are implemented in source. There is no 'newPendingTransactions' subscription, although some client docs imply otherwise. MaxSubscriptionsNewHead caps newHeads subscriptions. +**Sei-specific behavior:** Available over WebSocket only. SubscriptionAPI is not registered on the HTTP server, so over HTTP the method returns rpc.ErrNotificationsUnsupported. Only 'newHeads' and 'logs' are implemented in source. There is no 'newPendingTransactions' subscription, although some client docs imply otherwise. The number of concurrent subscriptions is capped per connection: `max_subscriptions_new_head` (`evm.max_subscriptions_new_head`) caps `newHeads` subscriptions, and `max_subscriptions_logs` (`evm.max_subscriptions_logs`, default 1000) caps concurrent `logs` subscriptions. Once the logs cap is reached, a new `logs` subscription is rejected with `no new subscription can be created`. **Parameters:** From 5e373a9b4be11ab6095a7cd10ebac941cd7ef6d6 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:11:28 +0000 Subject: [PATCH 06/43] docs: A new 'littidx' backend option is now accepted for the receipt-store configuration, adding a LittDB-based receipt store with a pebble tag index for eth_getLogs filtering. (sei-protocol/sei-chain#3620) --- node/giga-storage-migration.mdx | 11 +++++++---- node/node-operators.mdx | 21 ++++++++++++++++----- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/node/giga-storage-migration.mdx b/node/giga-storage-migration.mdx index 76ce52f..15ce5b9 100644 --- a/node/giga-storage-migration.mdx +++ b/node/giga-storage-migration.mdx @@ -419,7 +419,10 @@ In the `localnode` and `rpcnode` configuration scripts, setting `RECEIPT_BACKEND` environment variable explicitly. The explicit value takes precedence over the default. -`pebbledb` (also called `pebble`) is now the only supported receipt-store -backend. The former `parquet` option was removed. A `RECEIPT_BACKEND=parquet` -setting (or `rs-backend = "parquet"` in `app.toml`) is rejected with an error -(`unsupported receipt-store backend "parquet"; supported: pebbledb`). +The supported receipt-store backends are `pebbledb` (also called `pebble`) and +`littidx`. The `littidx` backend stores receipt bodies in LittDB with a pebble +tag index for `eth_getLogs` filtering, honoring the `KeepRecent` and +`PruneIntervalSeconds` config for retention and pruning. The former `parquet` +option was removed. An unsupported value for `RECEIPT_BACKEND` (or +`rs-backend` in `app.toml`) is rejected with an error +(`unsupported receipt-store backend "parquet"; supported: pebbledb, littidx`). diff --git a/node/node-operators.mdx b/node/node-operators.mdx index f9ea999..d455104 100644 --- a/node/node-operators.mdx +++ b/node/node-operators.mdx @@ -106,15 +106,26 @@ ss-backend = "pebbledb" ss-keep-recent = 100000 [receipt-store] -# Storage backend for EVM transaction receipts. pebbledb (aka pebble) is the -# only supported backend. +# Storage backend for EVM transaction receipts. Supported backends: pebbledb +# (aka pebble) and littidx. rs-backend = "pebbledb" ``` -`pebbledb` (also called `pebble`) is the only supported receipt-store backend. -The setting `rs-backend = "parquet"` (or `RECEIPT_BACKEND=parquet`) is rejected -with the error `unsupported receipt-store backend "parquet"; supported: pebbledb`. +The receipt-store backend accepts `pebbledb` (also called `pebble`) and +`littidx`. An unsupported value such as `rs-backend = "parquet"` (or +`RECEIPT_BACKEND=parquet`) is rejected with the error `unsupported receipt-store +backend "parquet"; supported: pebbledb, littidx`. + +`littidx` is a LittDB-based backend that stores receipt bodies in LittDB's +immutable append-only segments alongside a PebbleDB tag index used to answer +`eth_getLogs` filters without scanning whole blocks. It honors the same +`KeepRecent` (derived from `min-retain-blocks`) and `prune-interval-seconds` +retention and pruning settings as the PebbleDB backend. Because receipt bodies +are buffered in memory and flushed periodically rather than written through a +WAL, a hard crash can lose the most recently written receipt bodies; this is an +accepted tradeoff for auxiliary, non-consensus RPC data. + In the localnode and rpcnode configuration scripts, if you enable Giga Storage (`GIGA_STORAGE=true`) and `RECEIPT_BACKEND` is not set, the receipt backend defaults to `pebble`. The former `receipt-store.tx-index-backend` config field From 648c233d9adfef98592cc7c3d7869b830ea0c4cf Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:12:37 +0000 Subject: [PATCH 07/43] docs: The eth_createAccessList JSON-RPC endpoint now enforces a request rate limiter and can reject requests with a 'server busy' error when the limit is exceeded. (sei-protocol/sei-chain#3646) --- evm/reference.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/reference.mdx b/evm/reference.mdx index 70f09b2..f5ad4a0 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -1119,7 +1119,7 @@ The `filter` object applies only to `logs` subscriptions. For `newHeads`, pass t **Supported.** Generates an EIP-2930 access list (and gas used) for a transaction. -**Sei-specific behavior:** Defaults to the pending block tag, as geth does. If a VM error occurs during simulation, the method reports it in the result's 'error' field instead of failing the RPC. +**Sei-specific behavior:** Defaults to the pending block tag, as geth does. If a VM error occurs during simulation, the method reports it in the result's 'error' field instead of failing the RPC. A fail-fast request limiter may reject the call with 'eth_createAccessList rejected due to rate limit: server busy' when the limiter is saturated, as with `eth_call`, `eth_estimateGas`, and `eth_estimateGasAfterCalls`. **Parameters:** From 3b24f946a7be97c992dc841000ffda9b245a4c9c Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:13:45 +0000 Subject: [PATCH 08/43] docs: Adds a new node config field `hash-vault-disabled-unsafe` that lets operators disable the HashVault app-hash equivocation guard, and introduces HashVault recovery guidance for startup panics involving hash mismatches. (sei-protocol/sei-chain#3602) --- node/technical-reference.mdx | 22 ++++++++++++++++++++ node/troubleshooting.mdx | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 0b42f0b..03c4214 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -263,6 +263,28 @@ Any message whose fields exceed these limits is rejected at decode time, so an o Giga replaces the CometBFT mempool, so Giga does not support the `unsafe_flush_mempool` RPC endpoint. The endpoint returns `unsafe_flush_mempool is not supported with autobahn mempool`. + + +#### HashVault app-hash equivocation guard + +When a node runs under Autobahn (GigaRouter), the GigaRouter builds and owns an app-hash equivocation guard called HashVault. As each finalized height is executed, HashVault records that height's committed app hash before the implied state is committed. If the node ever attempts to commit a different app hash for a height it has already finalized, HashVault detects the conflict and halts the node. This protects a validator against externalizing two different app hashes for the same height, which could otherwise lead to slashing. + +HashVault is enabled by default on both Autobahn validators and fullnodes. It stores committed app hashes in a durable Pebble DB under the Autobahn persistent state directory, at `/hashvault` (see the `--persistent-state-dir` flag and the `PersistentStateDir` config field above). When Autobahn runs in memory only (no persistent state directory, for example in tests), HashVault falls back to a no-op with no equivocation protection. + + + If a node hits a startup panic reporting a HashVault app-hash mismatch, **do not restart it without human investigation** — the node attempted to change its mind about an already-finalized height. The panic and the preceding HashVault error log the conflicting hashes and the on-disk HashVault data directory (`hashVaultDir`). + + Only if you are certain there is no real equivocation, you can recover by stopping the node, deleting the HashVault data directory shown in the panic message, and restarting. **This removes equivocation protection**: if the node then commits a conflicting hash for an already-finalized height, the validator may be slashed. + + +You can disable HashVault entirely with the top-level `hash-vault-disabled-unsafe` field in `config.toml` (default `false`). This is an explicit, last-resort operator decision to run **without** equivocation protection; a node started with it enabled logs error-level warnings on every startup. Prefer the recovery steps above — only set `hash-vault-disabled-unsafe = true` if you are very sure the stored hashes are wrong and you keep hitting the same panic on new blocks. + +```toml +# config.toml — top-level key, before any [section] header. +# Leave false to keep equivocation protection enabled (recommended). +hash-vault-disabled-unsafe = false +``` + ### Key management Proper key management is critical for security. Use these commands to manage diff --git a/node/troubleshooting.mdx b/node/troubleshooting.mdx index 7135cff..46bf601 100644 --- a/node/troubleshooting.mdx +++ b/node/troubleshooting.mdx @@ -427,3 +427,43 @@ failed to initialize database: resource temporarily unavailable This means that you did not shut down the node properly. In that case, try to shut down or kill the `seid` process directly. If this does not help, restart your machine. Then try the rollback steps again. + + + +## HashVault app-hash equivocation panic + +Autobahn validators and fullnodes run an app-hash equivocation guard called HashVault, enabled by default. It stores the app hashes the node has committed in a durable database under `/hashvault`. If the node is ever about to commit a different app hash for a height it has already finalized, HashVault halts the node to prevent it from "changing its mind" about a finalized block. + +When the guard trips, you will see a panic in the logs similar to: + +```text +Hashvault detected app hash mismatch; node attempted to change its mind. DO NOT RESTART WITHOUT HUMAN INVESTIGATION. ... +blockHeight=[HEIGHT] existingHex=[...] incomingHex=[...] hashVaultDir=[/path/to/hashvault] +``` + + +**Do not restart the node without human investigation.** A HashVault mismatch can indicate a genuine equivocation. Restarting blindly, or removing the guard, can cause your validator to externalize a conflicting hash for an already-finalized height, which risks **slashing**. + + +**Investigate first.** Determine whether the mismatch reflects a real equivocation (for example, the same validator key running on more than one machine, or committed state that diverged from what the vault recorded) or a benign situation such as an out-of-band rollback or restore that left the committed app state inconsistent with the vault's history. + +### Recovery (only if you are certain there is no real equivocation) + +If, and only if, you are certain the stored hashes are wrong and there is no real equivocation, you can bypass the guard: + +1. **Stop the node.** +2. **Delete the HashVault data directory** shown as `hashVaultDir` in the panic message (by default `/hashvault`). +3. **Restart the node.** It starts with an empty equivocation history and re-commits hashes as it re-executes blocks. + +Deleting this directory removes equivocation protection for the heights it covered. If the node then commits a conflicting hash for a height it has already finalized, the validator may be slashed. + +### Last-resort: disabling HashVault + +If you repeatedly hit the same panic on new blocks and are very sure the stored hashes are totally wrong, you can run the node with the guard disabled by setting the top-level `hash-vault-disabled-unsafe` field in `config.toml`: + +```toml +# config.toml (top-level, before any [section] header) +hash-vault-disabled-unsafe = true +``` + +This runs the node **without any app-hash equivocation protection** and logs error-level warnings on every startup. The default is `false`, and you should re-enable the guard (set it back to `false` or remove the line) as soon as the underlying issue is resolved. From 1a0e2c2b2b2b185baa70f74fddabd8b7619c2d89 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:14:19 +0000 Subject: [PATCH 09/43] docs: Adds a new receipt-store config field/flag `receipt-store.log-filter-parallelism` (default 16) that bounds how many blocks a single eth_getLogs query scans concurrently in the littidx backend. (sei-protocol/sei-chain#3652) --- node/advanced-config-monitoring.mdx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index c46da6d..e504f0c 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -614,6 +614,19 @@ enable-read-write-metrics = true ``` + +## Tuning littidx eth_getLogs parallelism + +When the receipt store uses the `littidx` backend, each `eth_getLogs` query scans the requested block range across a bounded worker pool instead of one block at a time. Per-block tag index scans and litt body reads are independent, so fanning them across multiple workers reduces latency on wide-range queries while results stay in strict `(block, txIndex)` order. + +The `log-filter-parallelism` config key under `[receipt-store]` in `app.toml` bounds how many blocks a single query scans concurrently. It defaults to `16` and applies only to the `littidx` backend. A value `<= 0` falls back to the default. + +```toml +[receipt-store] +log-filter-parallelism = 16 +``` + + ## LittDB OpenTelemetry metrics LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` instead of a private Prometheus client. When `MetricsEnabled` is set, LittDB configures a Prometheus exporter on the global provider and serves `/metrics` on `MetricsPort` (default `9101`). The `MetricsNamespace` and `MetricsRegistry` config fields were removed, and all metric names use a fixed `litt_` prefix. From 6a08627c8a380a8b7d65e47b943d9630b1fc63e7 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:15:09 +0000 Subject: [PATCH 10/43] docs: The debug_traceStateAccess JSON-RPC method now honors the configured TraceTimeout and caps retained per-module state-access payload at 4 MiB (adding a 'truncated' field to the response), changing behavior to prevent OOM and unbounded traces. (sei-protocol/sei-chain#3653) --- evm/reference.mdx | 27 +++++++++++++++++++++++++++ evm/tracing/index.mdx | 2 ++ 2 files changed, 29 insertions(+) diff --git a/evm/reference.mdx b/evm/reference.mdx index f5ad4a0..7f9b6f8 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -1248,6 +1248,33 @@ The max block lookback guard (`max_trace_lookback_blocks`) applies. If a request } ``` +#### `debug_traceStateAccess` + +**Supported.** Replays a transaction by hash and returns the per-module state (KVStore) accesses it made, used for prestate derivation and state-access analysis. + +**Sei-specific behavior:** Available over HTTP only. The `max_trace_lookback_blocks` historical guard applies (see `debug_traceTransaction`). The call honors the configured `TraceTimeout`: replay and the subsequent prestate/trace serialization bail out when the trace deadline elapses, failing with a `context deadline exceeded` error instead of running unbounded. This prevents long-running replays from consuming resources without limit. + +Each module entry in the response carries a `truncated` boolean. Sei caps the key/value payload retained per module in the state-access log at 4 MiB. Once that cap is reached, the module's log is marked `truncated: true` and further accesses are dropped from the retained log, bounding a pathological transaction (such as a huge iterator scan) that would otherwise grow the response without limit. When a module is truncated, its `reads` and `has` entries reflect only the retained prefix, but its `stats` (operation counts and durations) stay complete and accurate, because they are accumulated at record time regardless of truncation. + +**Parameters:** + +| # | Name | Type | Description | +| :- | :- | :- | :- | +| 1 | `hash` | DATA, 32 bytes | Transaction hash to trace. | + +**Example request:** + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_traceStateAccess", + "params": [ + "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060" + ] +} +``` + #### `debug_traceCall` **Supported.** Executes and traces a call against a block's state without creating a transaction. diff --git a/evm/tracing/index.mdx b/evm/tracing/index.mdx index 4edd7ec..72ea160 100644 --- a/evm/tracing/index.mdx +++ b/evm/tracing/index.mdx @@ -56,6 +56,8 @@ Debug tracing is your primary tool to understand how EVM transactions execute on | `debug_traceBlockByNumber` | Trace an entire block | Block-level analysis | | `debug_traceCall` | Simulate and trace | Testing before execution | | `debug_traceStateAccess` | State access patterns | Performance optimization | + +`debug_traceStateAccess` respects the node's configured `TraceTimeout`. If the trace deadline elapses during replay or serialization, the call aborts with a `context deadline exceeded` error instead of running unbounded. Additionally, the per-module state-access log is capped at 4 MiB of retained key/value payload. When a module exceeds this cap, its trace dump sets `"truncated": true`: the `reads`/`has` fields then reflect only the retained prefix, while the `stats` (operation counts and durations) remain complete and accurate. | `debug_traceTransactionProfile` | Trace plus timing/store-access profiling | Latency breakdown and DB-access analysis | ## Transaction analysis example From 22541886e9cd5e6368b4f2517fd131fb067a3b39 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:16:01 +0000 Subject: [PATCH 11/43] docs: Adds two new EVM RPC config fields (max_request_body_bytes and max_concurrent_request_bytes) plus a new Prometheus metric to bound JSON-RPC request body sizes and concurrent request memory via pre-decode admission control. (sei-protocol/sei-chain#3648) --- node/advanced-config-monitoring.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index e504f0c..dc93ac8 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -517,6 +517,7 @@ The EVM RPC layer emits OpenTelemetry metrics through the process-wide `MeterPro | `evmrpc_websocket_connects_total` | Counter | Number of new WebSocket connections. | | `evmrpc_redirected_requests_total` | Counter | Number of EVM RPC requests forwarded to another validator. Labeled by `endpoint` and `connection`. | | `evmrpc_historical_debug_trace_attempts_total` | Counter | Number of `debug_trace*` requests that target historical blocks beyond the configured max block lookback. Labeled by `endpoint` and `connection`. | +| `evmrpc_requests_rejected_total` | Counter | Number of HTTP JSON-RPC requests rejected by pre-decode admission control. Labeled by `reason`, which is `oversize` when a request body exceeds the configured `max_request_body_bytes` cap (rejected with HTTP 413) or `busy` when the `max_concurrent_request_bytes` budget is exhausted (rejected with HTTP 429). | The `evmrpc_request_latency_seconds` histogram carries these labels: From bc4e0a6ea5a521b4a06e6b3c659f2758bcb10f0c Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:17:19 +0000 Subject: [PATCH 12/43] docs: Adds a per-block hash logger feature to the state-commit store with five new app.toml config fields (sc-hash-logger-*), enabled by default, for recording named block hashes for debugging/forensics. (sei-protocol/sei-chain#3647) --- node/advanced-config-monitoring.mdx | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index dc93ac8..d9590ff 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -628,6 +628,36 @@ log-filter-parallelism = 16 ``` + +## Per-block hash logger + +The state-commit store can record a per-block CSV of named block hashes to disk as a debugging and forensics tool. For each committed block it logs the memIAVL per-module and root hashes, the flatKV per-DB and root hashes, the application hash, the Tendermint block hash, the result hash (the merkle root over the block's deterministic transaction results, equal to the next block's `LastResultsHash`), and the changeset hash. Recording the same hashes under one block number on every node makes it straightforward to pinpoint where and when block-hash computation diverges across nodes. + +The feature is **enabled by default**. By default it writes files into a `hash.log` directory under the state-commit store's data directory (that is, `/data/hash.log`). + +### Configuration + +The hash logger is configured with five `app.toml` fields under `[state-commit]`: + +| Config key | Default | Description | +| --- | --- | --- | +| `sc-hash-logger-enable` | `true` | Turns per-block hash logging on or off. | +| `sc-hash-logger-directory` | empty | Directory for hash log files. When empty, defaults to `/data/hash.log`. | +| `sc-hash-logger-blocks-to-retain` | `0` | Number of most-recent blocks to keep on disk. `0` disables block-count retention (the disk-size cap is then the only bound). | +| `sc-hash-logger-target-file-size` | `16 MB` | Size in bytes a log file may reach before it is sealed and a new one is opened. Must be greater than `0`. | +| `sc-hash-logger-max-disk-size` | `16 GB` | Backstop cap in bytes on the total size of sealed log files. `0` disables the disk-size cap (block-count retention is then the only bound). | + +Retention is disk-driven by default: up to roughly 16 GiB of sealed files are kept, with block-count retention disabled. If both `sc-hash-logger-blocks-to-retain` and `sc-hash-logger-max-disk-size` are set to `0`, no retention bound applies and the logs grow without limit — a deliberate operator choice. + +For example, to disable the hash logger, add the following to `app.toml`: + +```toml +[state-commit] +sc-hash-logger-enable = false +``` + + + ## LittDB OpenTelemetry metrics LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` instead of a private Prometheus client. When `MetricsEnabled` is set, LittDB configures a Prometheus exporter on the global provider and serves `/metrics` on `MetricsPort` (default `9101`). The `MetricsNamespace` and `MetricsRegistry` config fields were removed, and all metric names use a fixed `litt_` prefix. From 04a4cf4635db054f2f0d89254a7d357a96152fe9 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:17:54 +0000 Subject: [PATCH 13/43] docs: LittDB's garbage collection was rewritten to run asynchronously with a durable gc-watermark file, adding six new config fields, changing the default GC period, and removing the CacheAwareGet API. (sei-protocol/sei-chain#3645) --- node/advanced-config-monitoring.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index d9590ff..7232a54 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -668,7 +668,7 @@ LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` | --- | --- | --- | --- | | `litt_table_size_bytes` | Gauge | bytes | The size of individual tables in the database. | | `litt_table_key_count` | Gauge | count | The number of keys in individual tables in the database. | -| `litt_open_iterator_count` | Gauge | count | The number of currently open iterators for individual tables in the database. A persistently nonzero value indicates a leaked iterator, which suspends garbage collection for the table. | +| `litt_open_iterator_count` | Gauge | count | The number of currently open iterators for individual tables in the database. A persistently nonzero value indicates a leaked iterator. Garbage collection proceeds while iterators are open — an iterator pins its snapshot segments via reservations so their files survive until it closes — so a leaked iterator pins those segment files on disk indefinitely rather than suspending garbage collection. | | `litt_bytes_read` | Counter | bytes | The number of bytes read from disk since startup. | | `litt_keys_read` | Counter | count | The number of keys read from disk since startup. | | `litt_cache_hits` | Counter | count | The number of cache hits since startup. | From a84ba06624b86a0b072c1a7ef6ffa67ef9ee2aff Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:05:57 +0000 Subject: [PATCH 14/43] docs: Introduces an experimental SEI_CONFIG_MANAGER environment variable that selects the config manager for seid; unset/'legacy' uses the existing path, 'v2' is a not-yet-implemented stub that errors, and invalid values fail hard. (sei-protocol/sei-chain#3671) --- node/technical-reference.mdx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 03c4214..9b56933 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -36,6 +36,27 @@ seid tendermint show-validator seid query node info ``` + +#### Experimental config manager selection (`SEI_CONFIG_MANAGER`) + +`seid` reads the experimental `SEI_CONFIG_MANAGER` environment variable to select which configuration manager resolves a node's configuration at startup. The value is matched exactly — it is not trimmed or case-folded. + +- Unset or `legacy`: uses the existing legacy config loader. This is the default and leaves the configuration path unchanged. +- `v2`: selects the new sei-config-backed manager. This manager is not yet implemented and fails fast with a hard error (`SEI_CONFIG_MANAGER=v2 not yet implemented`) rather than silently falling back to the legacy path, so a `v2` invocation is observable. +- Any other value: `seid` refuses to start and reports an `invalid SEI_CONFIG_MANAGER` error naming the legal tokens (unset, `legacy`, or `v2`). There is no silent fallback. + +```bash +# Default behavior (legacy loader) — no variable set +seid start + +# Equivalent to the default +SEI_CONFIG_MANAGER=legacy seid start + +# Selects the v2 manager, which currently returns a hard error +SEI_CONFIG_MANAGER=v2 seid start +``` + + #### Freeze mode (`--freeze-height`) As of v6.6.3, you can put a full node into read-only freeze mode at a specified block height. Use the `--freeze-height` start flag or the corresponding `freeze-height` field in `app.toml`. Freeze mode exists for historical RPC nodes. A node frozen at an upgrade height keeps running the pre-upgrade binary and keeps serving the state that binary produced. It does not shut down at the boundary or execute the upgrade block with code that no longer matches that state. From d5f10e8ecbf9fb62304511a53b30cfc3fbb736f7 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:06:41 +0000 Subject: [PATCH 15/43] docs: Adds new gRPC server config fields in app.toml (max-recv-msg-size, max-open-connections, keepalive/connection-age settings) with bounded defaults applied even when absent from older config files. (sei-protocol/sei-chain#3641) --- node/technical-reference.mdx | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 9b56933..3d8f0b7 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -385,10 +385,40 @@ ss-enable = true ss-backend = "pebbledb" ss-keep-recent = 100000 ss-prune-interval = 600 + +# gRPC server configuration +[grpc] +enable = true +address = "0.0.0.0:9090" +# Maximum message size in bytes the server can receive. Bounds per-request memory +# allocation before the rate limiter fires. Default 4 MB (4194304). +max-recv-msg-size = 4194304 +# Maximum number of simultaneous open connections. 0 means unlimited. +max-open-connections = 1000 +# Duration after which an idle connection is closed. 0 means infinity. +max-connection-idle = "5m0s" +# Maximum duration a connection may exist before it is closed. 0 means infinity. +max-connection-age = "0s" +# Additive grace period after max-connection-age during which the connection is +# forcibly closed. 0 means infinity. +max-connection-age-grace = "0s" +# Interval after which, with no activity, the server pings the client for liveness. +keepalive-time = "2h0m0s" +# Duration the server waits for a keepalive ping ack before closing the connection. +keepalive-timeout = "20s" +# Minimum interval a client must wait between keepalive pings; more frequent pings +# are penalized. +keepalive-min-time = "5m0s" +# Whether the server allows keepalive pings even when there are no active streams. +keepalive-permit-without-stream = false ``` + + The `[grpc]` server applies bounded defaults even when these keys are absent from an older `app.toml`. A node upgrading with a config file that predates these fields gets the in-code defaults (for example, a 4 MB `max-recv-msg-size`, `1000` `max-open-connections`, and a `5m` `max-connection-idle`) rather than running with unlimited connections or message sizes. A negative duration override for a keepalive or connection-age field is treated as a misconfiguration and clamped back to its safe default. The `max-connection-age` and `max-connection-age-grace` fields default to `0` (gRPC's "infinity"), and the keepalive-time/timeout/min-time defaults mirror gRPC's own defaults, so they are opt-in and do not change behavior unless configured. + + ### Config.toml parameters The `config.toml` file controls the core consensus engine and networking: From 2ab34ae5d7aed4d493665230c30c4571fc20feb3 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:07:31 +0000 Subject: [PATCH 16/43] docs: The seidb dump-flatkv command gains new flags (--lthash, --lthash-only, --read-limit-mb) for computing/verifying lattice hashes and throttling scan throughput, and --output-dir is no longer required when using --lthash-only. (sei-protocol/sei-chain#3665) --- node/technical-reference.mdx | 36 ++++++++++++++++++++++++++++++++++++ node/troubleshooting.mdx | 23 +++++++++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 3d8f0b7..491f0fd 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -201,6 +201,42 @@ seidb evm-logical-digest --backend memiavl \ Two backends match when the FlatKV `FINAL_DIGEST` equals the memIAVL `FINAL_DIGEST`. FlatKV also writes an internal migration-version marker row, which a memiavl-only node never owns. The command automatically omits that row from the final comparison. + + +#### Dumping FlatKV state and verifying its lattice hash + +The `dump-flatkv` subcommand dumps every physical `(key, value)` pair of a FlatKV store into per-bucket files, formatted to match `dump-iavl` so the same diff tooling works on both. It can optionally also compute the per-bucket and total LtHash (lattice hash) over the scanned state and verify that total against the committed root recorded in snapshot metadata. + +The command opens an independent read-only clone of the store (the snapshot is hard-linked and the changelog WAL is replayed into a temporary directory under the data dir), so it is safe to run against a live, block-producing node. The scan is throttled by `--read-limit-mb` so a dump against a running node does not starve the chain of disk bandwidth. + +```bash +# Latest version, all buckets, default 64 MiB/s throttle, LtHash + verify. +seidb dump-flatkv -d /.sei/data/state_commit/flatkv -o /tmp/flatkv-dump + +# Pin a specific height. +seidb dump-flatkv -d /.sei/data/state_commit/flatkv -o /tmp/flatkv-dump \ + --height 216890000 + +# Offline / idle disk: go full speed, skip LtHash. +seidb dump-flatkv -d /.sei/data/state_commit/flatkv -o /tmp/flatkv-dump \ + --read-limit-mb 0 --lthash=false + +# Verify the FlatKV lattice hash only, without writing key/value dump files. +seidb dump-flatkv -d /.sei/data/state_commit/flatkv --lthash-only +``` + +The command accepts these flags: + +- `--db-dir` (`-d`): The FlatKV data directory (the directory containing `current/`, `snapshot-*`, and `changelog/`). Required. +- `--output-dir` (`-o`): Where to write the per-bucket dump files (one file per bucket). Required unless `--lthash-only` is set. +- `--height`: The target version. `0` (the default) selects the latest available version by replaying the WAL to the tip. +- `--bucket` (`-b`): Restrict the on-disk dump to a single bucket (`account`, `code`, `storage`, or `legacy`). The default is all buckets. This only filters which hex files are written; the full keyspace is always scanned, and `--lthash` always covers all four buckets, so the LtHash total stays valid. +- `--lthash`: Compute the per-bucket and total LtHash over the scanned state and verify the total against committed snapshot metadata. Default `true`. The LtHash is always computed over all buckets regardless of `--bucket`, so the total matches the node's committed LtHash. +- `--lthash-only`: Compute and verify the LtHash without writing any bucket dump files. Default `false`. It requires `--lthash=true`, cannot be combined with `--bucket`, and does not require `--output-dir`. +- `--read-limit-mb`: Throttle the scan to at most this many MiB/s of `(key+value)` bytes read. Default `64`. A value of `0` disables throttling. Keep it low (default or less) on a shared or live node; raise it only for offline runs on idle disks. Negative values are rejected. + +With `--lthash`, the command prints an `LtHash (lattice hash)` block listing each bucket's count and checksum and the `TOTAL`, followed by a verification line comparing the re-scanned total against the committed snapshot metadata. A match prints `PASS`; a mismatch prints `FAIL` and the command exits non-zero. Verification is skipped when the selected snapshot predates LtHash metadata (its committed hash then covers only replayed WAL deltas, not full state) or when no committed LtHash is recorded at that version. + The command accepts these flags: - `--backend`: The backend to read (`flatkv` or `memiavl`). diff --git a/node/troubleshooting.mdx b/node/troubleshooting.mdx index 46bf601..7018597 100644 --- a/node/troubleshooting.mdx +++ b/node/troubleshooting.mdx @@ -114,9 +114,14 @@ systemctl restart seid The `dump-flatkv` command accepts these flags: - `--db-dir` (`-d`): The FlatKV database directory. -- `--output-dir` (`-o`): The output directory, with one file for each bucket. +- `--output-dir` (`-o`): The output directory, with one file for each bucket. Required unless `--lthash-only` is set. - `--height`: The FlatKV target version. The default, `0`, selects the latest available version. -- `--bucket` (`-b`): Restrict the dump to a single bucket (`account`, `code`, `storage`, or `legacy`). The default is all buckets. +- `--bucket` (`-b`): Restrict the dump to a single bucket (`account`, `code`, `storage`, or `legacy`). The default is all buckets. This only filters which files are written; the full keyspace is always scanned, and the LtHash always covers all four buckets. +- `--lthash`: Also compute the per-bucket and total LtHash (lattice hash) over the scanned state, and verify the total against the committed root recorded in snapshot metadata. The default is `true`. A mismatch exits non-zero. +- `--lthash-only`: Compute and verify the LtHash without writing any bucket dump files. Requires `--lthash=true`, and does not require `--output-dir`. The default is `false`. +- `--read-limit-mb`: Throttle the scan to at most this many MiB/s of (key+value) bytes read, so a dump against a running node does not starve the chain of disk bandwidth. The default is `64`. Set `0` for unlimited. Keep it at the default or lower on a shared/live node; raise it only for offline runs on idle disks. + +Because `dump-flatkv` opens an independent read-only clone of the store (the snapshot is hard-linked and the changelog WAL is replayed into a temp directory), it is safe to run against a live, block-producing node, and the `--read-limit-mb` throttle prevents it from starving the node of disk bandwidth. For example, to dump only the `storage` bucket at a specific version: @@ -124,6 +129,20 @@ For example, to dump only the `storage` bucket at a specific version: seidb dump-flatkv --db-dir $HOME/.sei/data/state_commit/flatkv --output-dir /home/ubuntu/flatkv-dump --height 12345678 --bucket storage ``` +To verify the FlatKV lattice hash against the committed snapshot metadata without writing any key/value dump files: + +```bash +seidb dump-flatkv --db-dir $HOME/.sei/data/state_commit/flatkv --lthash-only +``` + +For an offline run on an idle disk, go full speed and skip the LtHash: + +```bash +seidb dump-flatkv --db-dir $HOME/.sei/data/state_commit/flatkv --output-dir /home/ubuntu/flatkv-dump --read-limit-mb 0 --lthash=false +``` + +With `--lthash`, the output prints a per-bucket count and checksum plus a `TOTAL` LtHash, followed by a `LtHash verification vs snapshot metadata` PASS/FAIL line. Verification is skipped (rather than failing) when the selected snapshot predates LtHash metadata, since the committed hash then covers only replayed WAL deltas rather than full state. + ### Comparing EVM state between memIAVL and FlatKV When you debug an AppHash mismatch that involves EVM state, a byte-for-byte physical dump can diverge between backends, even when the underlying state is identical. This happens because every FlatKV value embeds a per-key block-height stamp (the height at which the key was last written or migrated). On a freshly migrated node, this stamp differs from the memIAVL leaf versions. From 2b6d1bef04e8e1e38612c3a3658f8e23bb1a14a4 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:08:44 +0000 Subject: [PATCH 17/43] docs: OTEL/Prometheus metrics now include a `chain_id` constant label on every emitted metric series, which affects dashboards and alerting queries for node operators. (sei-protocol/sei-chain#3692) --- node/advanced-config-monitoring.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index 7232a54..277a624 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -509,6 +509,12 @@ scrape_configs: The EVM RPC layer emits OpenTelemetry metrics through the process-wide `MeterProvider` (for example, a Prometheus exporter). It emits these metrics in parallel with the legacy `sei_*` metrics, so you can migrate dashboards incrementally. + + + +Every OpenTelemetry metric series exported through the Prometheus exporter (the `sei_chain` namespace, including the `evmrpc_*`, `flatkv_*`, `litt_*`, and `pebble_*` metrics documented below) now carries a `chain_id` constant label derived from the node's chain ID. The label is applied to every emitted series, so dashboards and alerting queries can group or filter by chain — for example when a single Prometheus instance scrapes nodes on more than one chain. Update any PromQL that aggregates across series (for example `sum(...) without (...)` or `by (...)` clauses) to account for the new label, and add a `chain_id` match where you want to scope a query to a single chain. + + ### Available EVM RPC metrics | Metric | Type | Description | From 6b3e429c31735ceec785675359cd3253c9e237a6 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:11:01 +0000 Subject: [PATCH 18/43] docs: Adds a new 'historical_replay' build tag that produces a consensus-unsafe binary variant using a lenient tx decoder (no body-bloat rejection) for replaying historical blocks. (sei-protocol/sei-chain#3691) --- node/technical-reference.mdx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 491f0fd..95f29fb 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -603,3 +603,21 @@ $HOME/.sei/ This reference guide gives essential technical information for operating Sei nodes and validators. For API documentation and other detailed specifications, see the relevant sections of this documentation. + + + +## Build tags + +### `historical_replay` + +The `historical_replay` build tag produces a consensus-unsafe `seid` variant intended solely for replaying historical blocks. When you build with this tag, the block-execution transaction decoder is swapped for a lenient protobuf decoder (`NewTxConfigWithoutBodyBloatRejection`) that does **not** reject non-canonical (body-bloat) transaction bodies. This lets a node decode and execute historical blocks whose transaction bodies predate strict body-bloat rejection. + +```bash +# Build a historical-replay seid variant +go build -tags historical_replay ... +``` + + + A binary built with the `historical_replay` tag is **consensus-unsafe** and must only be used for historical replay. Never run it on live or production paths. The lenient decoder is compiled in only when the tag is present, so an untagged (production) binary can never reach it — the lenient execution decoder stays off every mempool, `CheckTx`, and `DeliverTx` path. Do not use a `historical_replay` build to validate, produce blocks, or serve live traffic. + + From bf9c1033bb5c002e81568be7c24aadcfdf8d09cf Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:11:48 +0000 Subject: [PATCH 19/43] docs: Adds a new EVM RPC config field `evm.max_trace_struct_log_bytes` that caps retained struct-logger output per traced transaction on default debug_trace* endpoints, defaulting to 32 MiB (0 = unlimited). (sei-protocol/sei-chain#3677) --- evm/tracing/index.mdx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/evm/tracing/index.mdx b/evm/tracing/index.mdx index 72ea160..d4cd08c 100644 --- a/evm/tracing/index.mdx +++ b/evm/tracing/index.mdx @@ -445,6 +445,25 @@ These `[evm]` fields in `app.toml` control trace baking: Trace baking adds a persistent on-disk store at `/data/trace_db` and increases disk usage. The node flushes the store's write-ahead log when it shuts down cleanly. + + +## Struct-logger output cap + +The default `debug_trace*` endpoints (`debug_traceCall`, `debug_traceTransaction`, and `debug_traceBlockByNumber`/`debug_traceBlockByHash`) use the built-in struct logger when no custom tracer is supplied. Traces that read many distinct storage slots can retain large amounts of output, so the node bounds the retained struct-logger output per traced transaction. + +The `max_trace_struct_log_bytes` field under `[evm]` in `app.toml` controls this cap: + +| Field | Default | Description | +| ----- | ------- | ----------- | +| `max_trace_struct_log_bytes` | `33554432` (32 MiB) | Bounds the retained struct-logger output, in bytes, per traced transaction on the default `debug_trace*` endpoints. Set to `0` for unlimited (upstream geth behavior). | + +Behavior notes: + +- The bound is applied **per transaction**, not per RPC call. Because geth builds a fresh struct logger for each transaction, a `debug_traceBlock*` call over N transactions can retain up to N times this value (and the parallelized block-trace path holds several concurrent traces live). +- A caller-supplied `Limit` larger than `max_trace_struct_log_bytes` is clamped down to the configured value. +- A smaller caller-supplied `Limit` is honored unchanged. +- Custom tracers (for example `callTracer`, `prestateTracer`, or JavaScript tracers) are unaffected, as are requests when the cap is disabled (`0`). + ## Removed legacy trace filters The legacy `*ExcludeTraceFail` endpoints have been removed. For block tracing, use `debug_traceBlockByNumber` or `debug_traceBlockByHash`. For EVM receipts, use `eth_getTransactionReceipt`. There is no block or filter method to discover synthetic logs from Cosmos-originated transactions. If you already know a synthetic transaction hash, enable `sei_getTransactionReceipt` to get its receipt and logs. From 8f94f400ba20ccd93218dee400abfc9602c31487 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:12:50 +0000 Subject: [PATCH 20/43] docs: Adds a new `hashlog` command group to the seidb tool with `get-block` and `compare` subcommands for inspecting and diffing hash log archives produced by the hashlogger. (sei-protocol/sei-chain#3676) --- node/giga-storage-migration.mdx | 28 +++++++++++++++++++ node/technical-reference.mdx | 48 +++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/node/giga-storage-migration.mdx b/node/giga-storage-migration.mdx index 15ce5b9..00d64a8 100644 --- a/node/giga-storage-migration.mdx +++ b/node/giga-storage-migration.mdx @@ -357,6 +357,34 @@ When the migration finishes, each node also emits a `migration complete` summary log line and `seidb_migration_*` OpenTelemetry counters for the migrated keys and bytes. +### Inspecting hash log archives + +If you enable the per-block hash logger, the `seidb hashlog` command group +provides read-only tools for inspecting the on-disk hash log archives it +produces, so you can read or diff them without writing Go. + +- `seidb hashlog get-block ` prints every hash recorded for a + single block. A block with more than one record indicates it was executed more + than once (for example after a rollback), and each execution's hashes are + reported separately. Add `--json` to emit JSON instead of human-readable text. + + ```bash copy + seidb hashlog get-block [--json] + ``` + +- `seidb hashlog compare ` reports blocks whose hashes + differ between two archives — useful for pinpointing where two nodes diverged. + By default only the columns that differ are shown; pass `--full` to show every + column for each differing block. Restrict the comparison to a block range with + `--low` and `--high` (both must be supplied together), cap the number of + reported diffs with `--max-diffs` (`-1` for all), and add `--json` to emit + JSON. When the result is truncated at `--max-diffs`, the command warns that + there may be more differing blocks. + + ```bash copy + seidb hashlog compare [--low N --high M] [--max-diffs N] [--full] [--json] + ``` + ### Importing EVM state from memIAVL into FlatKV The `seidb import-flatkv-from-memiavl` command populates a FlatKV store from an existing memIAVL tree. It is for nodes that move to the FlatKV EVM commit store without a state sync. Two safety properties matter: diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 95f29fb..76f6c95 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -237,6 +237,54 @@ The command accepts these flags: With `--lthash`, the command prints an `LtHash (lattice hash)` block listing each bucket's count and checksum and the `TOTAL`, followed by a verification line comparing the re-scanned total against the committed snapshot metadata. A match prints `PASS`; a mismatch prints `FAIL` and the command exits non-zero. Verification is skipped when the selected snapshot predates LtHash metadata (its committed hash then covers only replayed WAL deltas, not full state) or when no committed LtHash is recorded at that version. + + +#### Inspecting hash log archives + +The `hashlog` command group provides read-only tools for inspecting the on-disk hash log archives produced by the hashlogger. Use it to pull the hashes recorded for a single block or to diff two archives without writing any Go code. + +##### Printing a single block's hashes + +The `get-block` subcommand prints every hash recorded for a single block in a hash log archive. + +```bash +# Print the hashes recorded for a block in an archive +seidb hashlog get-block + +# Emit JSON instead of human-readable text +seidb hashlog get-block --json +``` + +If the block was executed more than once (for example, after a rollback that replayed it), the archive holds several records for that block and each execution's hashes are reported separately. A hash type that was registered but not recorded for the block prints as `` (or serializes to JSON `null`, which is distinguishable from an absent type). + +##### Comparing two archives + +The `compare` subcommand compares two hash log archives and reports the blocks whose hashes differ between them. + +```bash +# Compare two archives over their full range +seidb hashlog compare + +# Restrict the comparison to a block range (both flags required together) +seidb hashlog compare --low --high + +# Cap the number of differing blocks reported +seidb hashlog compare --max-diffs + +# Show every column for each differing block, and emit JSON +seidb hashlog compare --full --json +``` + +The command accepts these flags: + +- `--low`: The lowest block to compare (inclusive). Requires `--high`. +- `--high`: The highest block to compare (inclusive). Requires `--low`. The `--low` and `--high` flags are optional, but must be supplied together; a one-sided range fails with an error. +- `--max-diffs`: The maximum number of differing blocks to report. The default is `-1`, which reports all of them. When the output is truncated at the cap, the command warns that there may be more differing blocks. +- `--full`: Show every column of every record for each differing block. The default is a compact view that shows only the columns that differ. This is also the only sensible rendering when the record counts differ between the two sides (a rollback re-executed the block a different number of times), since there is no single pair of records to diff column by column. +- `--json`: Emit JSON instead of human-readable text. The compact-by-default and `--full` column filtering apply to JSON output as well. + +When the archives are identical over the compared range, the command reports that and exits. + The command accepts these flags: - `--backend`: The backend to read (`flatkv` or `memiavl`). From 3ca40c3b88d50969552bc5e5bf0eff4ad9265146 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:13:52 +0000 Subject: [PATCH 21/43] docs: The state-commit sc-write-mode config now accepts the legacy "cosmos_only" value from v6.4/v6.5 app.toml files, mapping it to the current "memiavl_only" routing for backward-compatible upgrades. (sei-protocol/sei-chain#3704) --- node/giga-storage-migration.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/node/giga-storage-migration.mdx b/node/giga-storage-migration.mdx index 00d64a8..5a9099f 100644 --- a/node/giga-storage-migration.mdx +++ b/node/giga-storage-migration.mdx @@ -242,6 +242,14 @@ flip target after `migrate_bank` reports complete on the node (migration version 3, all modules in FlatKV). Then restart with `sc-write-mode = "flatkv_only"` to reach the terminal steady state. + +For backward compatibility with v6.4/v6.5 `app.toml` files, the +`sc-write-mode` config field (and the `--sc-write-mode` flag) still accepts the +legacy value `"cosmos_only"`. It is parsed as equivalent to `memiavl_only`, so +an older config that pins `sc-write-mode = "cosmos_only"` resolves to the same +memIAVL-only routing and continues to work after upgrading, with no edit +required before the node starts. + A correctness bug in the WAL replay path is fixed. On replay (catchup, read-only clone, snapshot export, and state-sync restore), the bug dropped empty (zero-length) values written with no delete flag. This made the FlatKV state and From 0c2c6943d8b9de43bc2138504f9245c71077f42c Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:14:37 +0000 Subject: [PATCH 22/43] docs: LittDB adds new config fields (FlushChannelSize, ShardControlChannelSize, AutoFlushByteThreshold, KeymapManagerMaxBatchBytes, MetricsServeEndpoint) and tightens the maximum value size to 2^32-1 bytes (~4 GiB), rejecting larger values that previously could span the boundary. (sei-protocol/sei-chain#3683) --- node/advanced-config-monitoring.mdx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index 277a624..aa61cbb 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -666,7 +666,12 @@ sc-hash-logger-enable = false ## LittDB OpenTelemetry metrics -LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` instead of a private Prometheus client. When `MetricsEnabled` is set, LittDB configures a Prometheus exporter on the global provider and serves `/metrics` on `MetricsPort` (default `9101`). The `MetricsNamespace` and `MetricsRegistry` config fields were removed, and all metric names use a fixed `litt_` prefix. +LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` instead of a private Prometheus client. When `MetricsEnabled` is set, LittDB records its metrics into the global provider. How those metrics are exported depends on `MetricsServeEndpoint` (default `false`): + +- When `MetricsServeEndpoint` is `false` (the default), LittDB records into the already-configured global `MeterProvider` and leaves exporting to the embedding application. The application is responsible for standing up the Prometheus exporter and serving the registry. No exporter or `/metrics` server is created by LittDB, and `MetricsPort` is ignored. +- When `MetricsServeEndpoint` is `true`, LittDB stands up its own Prometheus exporter on the global provider and serves `/metrics` on `MetricsPort` (default `9101`). + +`MetricsPort` is ignored unless both `MetricsEnabled` and `MetricsServeEndpoint` are `true`. The `MetricsNamespace` and `MetricsRegistry` config fields were removed, and all metric names use a fixed `litt_` prefix. ### Available LittDB metrics From 908455569348c12905452f0ffc0da1339761919e Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:15:22 +0000 Subject: [PATCH 23/43] docs: The seid node app now reads pruning options from flags and applies them to baseapp, enabling pruning configuration (e.g., --pruning) to actually take effect. (sei-protocol/sei-chain#3725) --- node/node-operators.mdx | 19 +++++++++++++++++++ node/technical-reference.mdx | 11 +++++++++++ 2 files changed, 30 insertions(+) diff --git a/node/node-operators.mdx b/node/node-operators.mdx index d455104..19812d9 100644 --- a/node/node-operators.mdx +++ b/node/node-operators.mdx @@ -787,6 +787,25 @@ The generated comments above do not describe two behaviors. First, everything. Second, when `evm-ss-db-directory` is unset, nodes with an existing `/data/evm_ss` directory keep using that legacy path. New nodes default to `/data/state_store/evm/{backend}`. + + +### Cosmos SDK state pruning + +Separate from the SeiDB retention settings above (`min-retain-blocks`, +`ss-keep-recent`), the node also honors the standard Cosmos SDK pruning options +(#3618). These control how much historical application state the baseapp keeps +and are applied to the node via `baseapp.SetPruning`: + +- `pruning` — the pruning strategy. Accepts `default`, `nothing` (archiving + node; keep all states), `everything` (keep only the most recent state), or + `custom` (use the `pruning-keep-recent` and `pruning-interval` values below). +- `pruning-keep-recent` — with `pruning = "custom"`, the number of recent + heights of state to keep. +- `pruning-interval` — with `pruning = "custom"`, how often (in blocks) pruning + is run. + +These can be supplied as `app.toml` settings or as the equivalent `seid` +start flags (e.g. `--pruning`, `--pruning-keep-recent`, `--pruning-interval`). diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 76f6c95..0a7c093 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -470,6 +470,17 @@ ss-backend = "pebbledb" ss-keep-recent = 100000 ss-prune-interval = 600 +# Base-layer (IAVL/baseapp) pruning. These Cosmos SDK pruning settings are read +# from the start flags/config and applied to the node's baseapp, so they are +# honored when the node runs. `pruning` selects a strategy: "default", "nothing" +# (archive node, keep all states), "everything" (keep only recent states), or +# "custom" (use the pruning-keep-recent and pruning-interval values below). +pruning = "default" +# Number of recent heights to keep on disk. Used with pruning = "custom". +pruning-keep-recent = "0" +# How often (in blocks) to run a pruning pass. Used with pruning = "custom". +pruning-interval = "0" + # gRPC server configuration [grpc] enable = true From 8b59ed1a06edeb439c7d3a525a9d756bb99f8417 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:16:56 +0000 Subject: [PATCH 24/43] docs: Adds two new EVM RPC config fields (max_state_override_accounts, max_state_override_slots) that cap the size of state overrides in eth_call, eth_estimateGas, eth_estimateGasAfterCalls, and debug_traceCall, with correct go-ethereum SetStorage overlay semantics. (sei-protocol/sei-chain#3722) --- evm/reference.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/evm/reference.mdx b/evm/reference.mdx index 7f9b6f8..de271da 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -1034,6 +1034,8 @@ The `filter` object applies only to `logs` subscriptions. For `newHeads`, pass t | 3 | `overrides` | object | Optional per-account state overrides (balance, code, nonce, state). | | 4 | `blockOverrides` | object | Optional block-context overrides (number, time, coinbase). | +State overrides are bounded in size. `max_state_override_accounts` (`evm.max_state_override_accounts`, default 100) caps the number of accounts in a single override, and `max_state_override_slots` (`evm.max_state_override_slots`, default 1000) caps the number of storage slots per account (applied to both `state` and `stateDiff`). Requests that exceed either cap are rejected with a 'state override has too many accounts' or 'too many slots' error. Setting a cap to 0 disables that limit. These caps apply equally to `eth_estimateGas`, `eth_estimateGasAfterCalls`, and `debug_traceCall`. + **Example request:** ```json From ba5bcb83b8d6a6544697516a35898c3b7f1824bb Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:17:39 +0000 Subject: [PATCH 25/43] docs: Prometheus telemetry is now automatically tagged with a chain_id global label and new metrics subsystems were added; internal metrics APIs were refactored to use global registries (not user-facing). (sei-protocol/sei-chain#3682) --- node/advanced-config-monitoring.mdx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index aa61cbb..5d8a358 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -505,6 +505,24 @@ scrape_configs: - targets: ['localhost:9100'] ``` + + +## Cosmos SDK / Tendermint telemetry chain_id label + +Node telemetry automatically injects a `chain_id` global label into the Cosmos SDK / Tendermint Prometheus telemetry (the `GlobalLabels` of the `[telemetry]` configuration) based on the node's client chain ID. The label is appended only when a `chain_id` label is not already configured in `GlobalLabels`, so an explicit operator-set value always takes precedence. This is distinct from the `chain_id` constant label applied to the OpenTelemetry `sei_chain` namespace described above — it covers the separate Cosmos SDK/Tendermint telemetry path. + +Because every emitted series carries this label, update any PromQL that aggregates across series (for example `sum(...) without (...)` or `by (...)` clauses) to account for `chain_id`, and add a `chain_id` match where you want to scope a query to a single chain — useful when one Prometheus instance scrapes nodes on more than one chain. + +### New Tendermint internal metrics subsystems + +Additional Prometheus metrics are exported under the `tendermint` namespace in these new subsystems: + +| Subsystem prefix | Description | +| --- | --- | +| `tendermint_internal_autobahn_avail_*` | Autobahn availability metrics, including the commit and app road index, the commit and app global block number, proposal-to-commit latency, and commit-to-commit latency (the latter labeled by `timeouts`). | +| `tendermint_internal_autobahn_data_*` | Autobahn data latency metrics (a `latency` histogram labeled by `resource` and `stage`) tracking resource processing from production to the given stage. | +| `tendermint_internal_p2p_mux_*` | p2p mux stream metrics, including stream `latency`, `in_flight` open streams, and per-stream `send_msgs`, `recv_msgs`, `send_bytes`, and `recv_bytes` counters (labeled by `role` and `kind`). | + ## EVM RPC OpenTelemetry metrics The EVM RPC layer emits OpenTelemetry metrics through the process-wide `MeterProvider` (for example, a Prometheus exporter). It emits these metrics in parallel with the legacy `sei_*` metrics, so you can migrate dashboards incrementally. From f8ba5297adfe00347e236e759072239637f265ca Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:12:01 +0000 Subject: [PATCH 26/43] docs: The seidb evm-logical-digest command gains a new 'composite' backend, a new --memiavl-open-mode flag (snapshot/replay), and new --flatkv-dir/--memiavl-dir flags for comparing EVM logical state across backends on a migrating node. (sei-protocol/sei-chain#3711) --- node/technical-reference.mdx | 9 ++++++--- node/troubleshooting.mdx | 24 +++++++++++++++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 0a7c093..ea17d32 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -287,11 +287,14 @@ When the archives are identical over the compared range, the command reports tha The command accepts these flags: -- `--backend`: The backend to read (`flatkv` or `memiavl`). -- `--db-dir` (`-d`): For FlatKV, the FlatKV data directory. For memIAVL, the memIAVL root directory that contains `current/` and `snapshot-*`. +- `--backend`: The backend to read (`flatkv`, `memiavl`, or `composite`). Use `composite` to digest the union of FlatKV and memIAVL rows for a node that is mid-migration, so it can be compared against a memiavl-only node at the same height. +- `--db-dir` (`-d`): For FlatKV, the FlatKV data directory. For memIAVL, the memIAVL root directory that contains `current/` and `snapshot-*`. Required unless `--backend composite` is used (composite mode uses `--flatkv-dir` and `--memiavl-dir` instead). +- `--flatkv-dir`: The FlatKV data directory in `composite` mode. Required with `--backend composite`. +- `--memiavl-dir`: The memIAVL root directory (containing `current/` and `snapshot-*`) in `composite` mode. Required with `--backend composite`. - `--height`: The target version. FlatKV WAL-replays to it, and memIAVL resolves `snapshot-/evm` (`0` selects the `current` symlink). +- `--memiavl-open-mode`: How memIAVL is read. `snapshot` (the default) is the fast path: it sequentially scans the completed snapshot kvs file and requires an on-disk snapshot at `--height` (or `--height 0` for the `current` symlink). `replay` is the slow path (roughly an order of magnitude slower): it opens a read-only DB, replays the changelog up to `--height`, then walks the mmap tree. Use `replay` only when no snapshot exists at the target height. Prefer `snapshot` whenever `--height` matches an existing snapshot boundary. - `--memiavl-normalization`: The memIAVL normalization mode. Use `semantic` or `independent` for the raw EVM key-value decoder, or `translator` for the current migration mapping. The default is `semantic`. -- `--inspect-bucket`: Inspect one normalized bucket (`account`, `code`, `storage`, or `legacy`) instead of printing the global digest. +- `--inspect-bucket`: Inspect one normalized bucket (`account`, `code`, `storage`, or `legacy`) instead of printing the global digest. It supports only `--memiavl-open-mode=snapshot`; combining it with `replay` returns an error. - `--key-offset` (inspect mode): The byte offset into the physical key, applied before `--key-prefix` or sharding. - `--key-prefix` (inspect mode): A hex prefix, relative to `--key-offset`, that filters physical keys. - `--shard-next-bytes` (inspect mode): Group matching keys by this many bytes after `--key-prefix`. diff --git a/node/troubleshooting.mdx b/node/troubleshooting.mdx index 7018597..72de811 100644 --- a/node/troubleshooting.mdx +++ b/node/troubleshooting.mdx @@ -159,7 +159,29 @@ seidb evm-logical-digest --backend memiavl \ --db-dir $HOME/.sei/data/state_commit/memiavl --height 213200000 ``` -Each run prints per-bucket `bucket_digest` values and a single `FINAL_DIGEST` line that covers the `account`, `code`, `storage`, and `legacy` buckets. Compare the `FINAL_DIGEST` lines from both backends at the same height. They should match. FlatKV can contain a FlatKV-only migration-version marker that a memIAVL-only node never owns. The command automatically omits that row from the FlatKV final result, so both results cover the same data. +The `--backend` flag accepts `flatkv`, `memiavl`, and `composite`. + +For memIAVL, `--memiavl-open-mode` controls how leaves are read: + +- `snapshot` (the default) sequentially scans the completed snapshot `kvs` file at `snapshot-/evm`. This is the fast path and requires an on-disk snapshot at that exact height (or `--height 0` for the current symlink). Prefer it whenever your target height matches an existing snapshot boundary. +- `replay` opens a read-only DB, replays the changelog up to `--height`, then walks the in-memory/mmap tree. It is roughly an order of magnitude slower than `snapshot`, so use it only when no snapshot exists at the target height (for example, when a node's snapshot rewrite lags the tip): + +```bash +# memIAVL digest for a height with no on-disk snapshot (slower): +seidb evm-logical-digest --backend memiavl --memiavl-open-mode replay \ + --db-dir $HOME/.sei/data/state_commit/memiavl --height 213205000 +``` + +On a node that is mid-migration, EVM state is split between the two backends: FlatKV holds the rows already migrated, while memIAVL still holds the rows not yet past the migration boundary. Use `--backend composite` to digest the union of both, so a migrating node can be compared against a memIAVL-only node at the same height. In `composite` mode, `--db-dir` is not required; instead provide the two backend directories with `--flatkv-dir` and `--memiavl-dir`. Because a live migrating node usually keeps memIAVL snapshots at heights outside FlatKV's retained window, pass `--memiavl-open-mode replay`: + +```bash +# Mid-migration node: digest the flatkv + memiavl union at one height: +seidb evm-logical-digest --backend composite --memiavl-open-mode replay \ + --flatkv-dir $HOME/.sei/data/state_commit/flatkv \ + --memiavl-dir $HOME/.sei/data/state_commit/memiavl --height 213200000 +``` + +Each run prints per-bucket `bucket_digest` values and a single `FINAL_DIGEST` line that covers the `account`, `code`, `storage`, and `legacy` buckets. Compare the `FINAL_DIGEST` lines from both backends at the same height. They should match. FlatKV can contain FlatKV-only migration marker rows that a memIAVL-only node never owns: the migration-version marker (present once a migration completes) and the migration-boundary cursor (present only while a migration is in flight). The command automatically omits both rows from the final result, so memIAVL, mid-migration, and completed nodes all produce comparable digests. For targeted debugging, the command can also inspect a single normalized bucket instead of printing the global digest. The [seidb tooling section of the technical reference](/node/technical-reference#comparing-evm-state-across-backends) has the full flag reference, including inspect mode, sharding, and `--find-hash`. These examples list the first 50 `account` rows with version metadata, and shard the `storage` bucket under a key prefix by the next 2 bytes: From fa86ef51f798e68e5b6027e77407bc934f473018 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:16:58 +0000 Subject: [PATCH 27/43] docs: New app.toml config fields gate which debug tracers callers may use on debug_trace* endpoints, restricting to a native allowlist and disabling request-supplied JavaScript tracers by default. (sei-protocol/sei-chain#3718) --- evm/reference.mdx | 2 ++ evm/tracing/index.mdx | 20 ++++++++++++++++++++ evm/tracing/javascript-tracers.mdx | 10 ++++++++++ evm/tracing/troubleshooting.mdx | 29 +++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/evm/reference.mdx b/evm/reference.mdx index de271da..94027d6 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -1167,6 +1167,8 @@ State overrides are bounded in size. `max_state_override_accounts` (`evm.max_sta **Sei-specific behavior:** Available over HTTP only, because the debug namespace is not registered on the WebSocket server. Supports geth tracers: callTracer, prestateTracer, flatCallTracer, and the struct (opcode) logger. TraceBaker pre-bakes and caches the results of callTracer, prestateTracer, and flatCallTracer. Requires trace-enabled or archive state for the target height. +**Tracer gating.** Caller-supplied `TraceConfig.Tracer` names are gated by `[evm]` config in `app.toml` (#3618). Only native tracer names listed in `trace_allowed_tracers` (`evm.trace_allowed_tracers`, default `["callTracer", "prestateTracer", "flatCallTracer", "4byteTracer", "noopTracer", "muxTracer"]`) may be requested; a name that is not in the list is rejected. Request-supplied JavaScript tracer source is rejected unless `trace_allow_js_tracers` (`evm.trace_allow_js_tracers`, default `false`) is enabled, and enabling it does not widen the native allowlist. When `muxTracer` is requested, its nested tracer names are validated recursively (bounded depth of 16). Omitting `tracer` uses the default struct logger, which is always available. This gating applies consistently across `debug_traceTransaction`, `debug_traceCall`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceTransactionProfile`. + The max block lookback guard (`max_trace_lookback_blocks`) applies. If a request's target block is older than the configured lookback, the request is rejected with an error of the form `block number X is beyond max lookback of Y`. Each such request increments the `evmrpc_historical_debug_trace_attempts_total` metric. This guard applies consistently across all `debug_trace*` endpoints. **Parameters:** diff --git a/evm/tracing/index.mdx b/evm/tracing/index.mdx index d4cd08c..6bc2972 100644 --- a/evm/tracing/index.mdx +++ b/evm/tracing/index.mdx @@ -447,6 +447,26 @@ These `[evm]` fields in `app.toml` control trace baking: +## Tracer allowlist + +Caller-supplied `tracer` values on the `debug_trace*` endpoints are gated by `[evm]` config in `app.toml`. By default, only a fixed set of native geth tracers may be requested, and request-supplied JavaScript tracer source is rejected. This deviates from upstream geth, which accepts JavaScript tracers by default. The gate applies to `debug_traceCall`, `debug_traceTransaction`, `debug_traceBlockByNumber`/`debug_traceBlockByHash`, and `debug_traceTransactionProfile`. When no `tracer` is supplied, the default struct logger remains available. + +| Field | Default | Description | +| ----- | ------- | ----------- | +| `trace_allowed_tracers` | `["callTracer", "prestateTracer", "flatCallTracer", "4byteTracer", "noopTracer", "muxTracer"]` | Native debug tracer names that callers may request via `TraceConfig.Tracer`. Validated native-only at startup — a non-native or misspelled name fails startup. Set to `[]` to disable all named tracers. Request-supplied JavaScript tracer source can never be enabled through this list. | +| `trace_allow_js_tracers` | `false` | Opts in to request-supplied JavaScript tracer source in `TraceConfig.Tracer`. This executes untrusted code in-process; keep disabled on public/default RPC nodes. Enabling it does **not** widen `trace_allowed_tracers`: native tracer names must still be listed there to be usable. | + +Behavior notes: + +- A tracer name that is not listed in `trace_allowed_tracers` is rejected, unless it is JavaScript source and `trace_allow_js_tracers` is enabled. +- JavaScript tracer source is rejected unless `trace_allow_js_tracers` is set to `true`. +- When `muxTracer` is requested, its nested tracer names are validated recursively against the same allowlist, with a bounded nesting depth of 16. +- `trace_bake_tracers` names are held to the same native-only rule and validated at startup, so a non-native or misspelled baked tracer fails startup instead of being evaluated as JavaScript source on every block. + +The `trace_bake_tracers` note above that eligible values are standard named tracers is subsumed by this native-only startup validation: entries must be native tracer names such as `callTracer`, `prestateTracer`, or `flatCallTracer`. + + + ## Struct-logger output cap The default `debug_trace*` endpoints (`debug_traceCall`, `debug_traceTransaction`, and `debug_traceBlockByNumber`/`debug_traceBlockByHash`) use the built-in struct logger when no custom tracer is supplied. Traces that read many distinct storage slots can retain large amounts of output, so the node bounds the retained struct-logger output per traced transaction. diff --git a/evm/tracing/javascript-tracers.mdx b/evm/tracing/javascript-tracers.mdx index c09de20..effbf31 100644 --- a/evm/tracing/javascript-tracers.mdx +++ b/evm/tracing/javascript-tracers.mdx @@ -5,6 +5,16 @@ keywords: ['javascript tracers', 'custom tracers', 'evm debugging', 'tracer opti --- JavaScript tracers let you create custom debugging logic to analyze EVM transactions on Sei. You can use them for more complex analysis than the built-in tracers support. + + + + **JavaScript tracers are disabled by default on Sei.** Unlike upstream geth, Sei does not accept request-supplied JavaScript tracer source on `debug_trace*` endpoints unless a node operator explicitly opts in. To enable them, set `trace_allow_js_tracers = true` under the `[evm]` section of `app.toml`. + + This is a deliberate security hardening: a JavaScript tracer executes caller-supplied code in-process on the node, so it should only be enabled on trusted or private RPC nodes — never on public/default RPC infrastructure. Enabling `trace_allow_js_tracers` does **not** widen the native tracer allowlist (`trace_allowed_tracers`); the two settings are independent. + + If `trace_allow_js_tracers` is `false` (the default), any request supplying JavaScript tracer source to `debug_traceCall`, `debug_traceTransaction`, `debug_traceBlockBy*`, or `debug_traceTransactionProfile` is rejected before any tracer is constructed. If you only need standard analysis, prefer the built-in native tracers (`callTracer`, `prestateTracer`, `flatCallTracer`, `4byteTracer`, `noopTracer`, `muxTracer`), which require no opt-in. + + ## Interface overview Every JavaScript tracer has this structure: diff --git a/evm/tracing/troubleshooting.mdx b/evm/tracing/troubleshooting.mdx index a7f083e..63f4bbf 100644 --- a/evm/tracing/troubleshooting.mdx +++ b/evm/tracing/troubleshooting.mdx @@ -142,6 +142,35 @@ This guide covers common issues with EVM transaction tracing on Sei and gives pr ### 4. Network and connection issues + + +#### Tracer not allowed / JavaScript tracers disabled + +**Error**: `debug tracer "..." is not allowed; JavaScript tracers are disabled and only native tracers listed in evm.trace_allowed_tracers may be used` + +**Cause**: Sei gates which tracers callers may request through `TraceConfig.Tracer` on the `debug_traceCall`, `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceTransactionProfile` endpoints. By default only the native tracers listed in the `[evm]` `trace_allowed_tracers` config are accepted, and request-supplied JavaScript tracer source is rejected. This deviates from upstream geth, which accepts JavaScript tracers by default. + +**Solutions**: + +- Use one of the allowlisted native tracers. The shipped default allowlist is: + + ```toml + [evm] + trace_allowed_tracers = ["callTracer", "prestateTracer", "flatCallTracer", "4byteTracer", "noopTracer", "muxTracer"] + ``` + +- If you are an operator and need to permit a native tracer that is not listed, add its name to `trace_allowed_tracers`. Only native geth tracer names are accepted here; a typo or JavaScript source causes the node to fail at startup. Setting the list to `[]` disables all named tracers. +- To allow request-supplied JavaScript tracer source, an operator must explicitly opt in: + + ```toml + [evm] + trace_allow_js_tracers = true + ``` + + This executes untrusted code in-process and should be kept disabled on public/default RPC nodes. Enabling it does **not** widen `trace_allowed_tracers`: native tracer names must still be listed there to be usable. + +When using `muxTracer`, the nested tracer names in `TracerConfig` are validated recursively against the same allowlist, with a bounded nesting depth of 16. A nested name that is not allowlisted produces a `nested debug tracer "..." is not allowed` error. Also note that `trace_bake_tracers` names are validated as native-only at startup, so a non-native or mistyped name there will fail node startup rather than being evaluated as JavaScript. + **Error**: `connection refused` or `network timeout` **Solutions**: From dd39c368ec78bfbc7d4ed84a3bff9612013fba7d Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:18:50 +0000 Subject: [PATCH 28/43] docs: LittDB adds a per-table Compression config option (S2 algorithm) that compresses values on disk, introduces a v4 segment metadata format, lowers the max value size to 2^32-2 bytes, and restricts secondary keys to full-value aliases on compressed tables. (sei-protocol/sei-chain#3769) --- node/advanced-config-monitoring.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index 5d8a358..87d51af 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -718,6 +718,11 @@ LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` | `litt_chunk_cache_weight_added_bytes` | Counter | bytes | The weight of the entries added to the chunk cache. | | `litt_chunk_cache_eviction_latency_seconds` | Histogram | seconds | Eviction latency of the chunk cache. | +| `litt_compression_latency_seconds` | Histogram | seconds | Latency of compressing a batch of values before they are written. Emitted only for tables with value compression enabled. | +| `litt_compression_uncompressed_bytes` | Counter | bytes | The number of uncompressed value bytes submitted to compression since startup. | +| `litt_compression_compressed_bytes` | Counter | bytes | The number of compressed value bytes produced by compression since startup. Compared against `litt_compression_uncompressed_bytes`, this gives the aggregate compression ratio and total bytes saved. | +| `litt_compression_ratio` | Histogram | ratio | The per-batch compression ratio (compressed bytes divided by uncompressed bytes); lower is better. | + ### Attributes | Attribute | Description | From 0555a66ab369752aeeb427ef86163864933b475a Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:24:09 +0000 Subject: [PATCH 29/43] docs: Adds a new node config field `max-search-scan-budget` (default 100,000) that bounds the total KV index entries all in-flight tx_search/block_search requests may visit, returning an error when the process-wide budget is exceeded. (sei-protocol/sei-chain#3708) --- node/technical-reference.mdx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index ea17d32..6204011 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -561,6 +561,19 @@ max-open-connections = 900 # greater than 0 it is applied as a context timeout on the request, so a # BroadcastTxCommit call will be cancelled if it does not complete within this duration. timeout-broadcast-tx-commit = "10s" +# Maximum number of results returned by tx_search and block_search. +# Set to 0 to disable the cap (not recommended on public nodes). +max-tx-search-results = 10000 +# max-search-scan-budget sets a process-wide cap on the total number of KV index +# entries that all in-flight tx_search and block_search requests may visit at +# once. It is shared across requests (not applied per-query) and bounds peak +# memory and scan CPU under broad or highly concurrent search load. When the +# budget is exhausted, in-flight searches fail with "kv indexer scan budget +# exceeded; narrow the query or retry later" rather than continuing to accumulate. +# The same budget is shared by the tx and block indexers, so the cap is +# process-wide across both. Default 100000; set to 0 to disable the cap (not +# recommended on public nodes). +max-search-scan-budget = 100000 # Mempool Configuration [mempool] From 6b4093db7ca3ee934e22b1bfe9e7e4eddf651099 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:25:05 +0000 Subject: [PATCH 30/43] docs: The autobahn consensus data layer migrates from the old WAL-based persistence to a LittDB-backed BlockDB, adding new config file fields and new CLI flags to the gen-autobahn-config command for tuning BlockDB retention and GC. (sei-protocol/sei-chain#3707) --- node/technical-reference.mdx | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 6204011..6e43d17 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -314,14 +314,37 @@ seid tendermint gen-autobahn-config [node-dirs...] --output # The --output flag may be abbreviated as -o seid tendermint gen-autobahn-config ./node0 ./node1 ./node2 -o autobahn.json -# Choose where autobahn consensus and data WALs are persisted (default: data/autobahn) +# Choose where autobahn consensus state and BlockDB are persisted (default: data/autobahn) seid tendermint gen-autobahn-config ./node0 ./node1 --output autobahn.json --persistent-state-dir data/autobahn -# Pass an empty value to disable persistence and run in-memory only +# Pass an empty value to disable persistence and run in-memory only (memblock) seid tendermint gen-autobahn-config ./node0 ./node1 --output autobahn.json --persistent-state-dir= + +# Tune BlockDB retention and GC +seid tendermint gen-autobahn-config ./node0 ./node1 --output autobahn.json --blockdb-retention 30s --blockdb-gc-period 10s +``` + +The `--persistent-state-dir` flag controls where Autobahn persists its consensus state and BlockDB across restarts. The default is `data/autobahn`, so persistence is enabled by default without any operator action. Autobahn's durable block and quorum-certificate storage now lives in a LittDB-backed BlockDB opened under `/blockdb`, replacing the previous data write-ahead logs (the old `globalblocks/` and `fullcommitqcs/` subdirectories are no longer read). At config load time, a relative path is resolved against the node's `--home` directory, and an absolute path is used as is. An empty value (`--persistent-state-dir=`) disables persistence entirely and both the consensus and data layers run in memory only (memblock). When set, the flag populates the `PersistentStateDir` field in the generated config. + + + Operators upgrading from a release that used the data WALs must be aware that Autobahn consensus state now lives under `/blockdb` (a LittDB BlockDB). The old `globalblocks/` and `fullcommitqcs/` WAL directories are no longer read. + + +Two additional flags tune the BlockDB: + +- `--blockdb-retention`: Sets the BlockDB retention TTL written into the `block_db` section of the generated config. The default is `30s` because this helper targets local/docker clusters rather than production node bring-up. Pass an empty value (`--blockdb-retention=`) to omit the field and keep littblock's production default of 24h. +- `--blockdb-gc-period`: Sets the BlockDB garbage-collection period (for example `10s`). Omit it to keep littblock's default GC period. + +When either flag is set, an optional `block_db` section is written into `autobahn.json`: + +```json +"block_db": { + "retention": "30s", + "gc_period": "10s" +} ``` -The `--persistent-state-dir` flag controls where Autobahn persists its consensus and data write-ahead logs (WALs) across restarts. The default is `data/autobahn`, so persistence is enabled by default without any operator action. The consensus and data layers write to distinct subdirectories under this shared on-disk root. At config load time, a relative path is resolved against the node's `--home` directory, and an absolute path is used as is. An empty value (`--persistent-state-dir=`) disables persistence entirely, and both the consensus and data layers run in memory only. When set, the flag populates the `PersistentStateDir` field in the generated config. +Each field is independently optional and is omitted from the JSON when empty; absent fields keep whatever littblock's default config uses. The `block_db` section overlays those defaults only when `persistent_state_dir` is set, and is ignored when persistence is disabled (memblock). When set, `retention` and `gc_period` must each be greater than zero. The command reads these files from each node directory: From 576ba510151cb4014fbd09b130cca4daf02fd814 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:11:31 +0000 Subject: [PATCH 31/43] docs: Adds eight new query precompiles (auth, authz, evidence, feegrant, mint, params, slashing, upgrade) and expands bank, distribution, and gov precompiles with many new read-only query methods, plus a new AssociatePubKey transaction on the addr precompile. (sei-protocol/sei-chain#3767) --- docs.json | 12 +- evm/precompiles/auth.mdx | 111 ++++++++++++ evm/precompiles/authz.mdx | 151 +++++++++++++++++ evm/precompiles/cosmwasm-precompiles/addr.mdx | 2 + evm/precompiles/cosmwasm-precompiles/bank.mdx | 90 ++++++++++ evm/precompiles/evidence.mdx | 85 ++++++++++ evm/precompiles/example-usage.mdx | 8 + evm/precompiles/feegrant.mdx | 96 +++++++++++ evm/precompiles/governance.mdx | 108 ++++++++++++ evm/precompiles/mint.mdx | 113 +++++++++++++ evm/precompiles/params.mdx | 69 ++++++++ evm/precompiles/slashing.mdx | 158 ++++++++++++++++++ evm/precompiles/upgrade.mdx | 132 +++++++++++++++ 13 files changed, 1133 insertions(+), 2 deletions(-) create mode 100644 evm/precompiles/auth.mdx create mode 100644 evm/precompiles/authz.mdx create mode 100644 evm/precompiles/evidence.mdx create mode 100644 evm/precompiles/feegrant.mdx create mode 100644 evm/precompiles/mint.mdx create mode 100644 evm/precompiles/params.mdx create mode 100644 evm/precompiles/slashing.mdx create mode 100644 evm/precompiles/upgrade.mdx diff --git a/docs.json b/docs.json index ce24c8a..37f6c4b 100644 --- a/docs.json +++ b/docs.json @@ -198,7 +198,15 @@ "evm/precompiles/cosmwasm-precompiles/bank", "evm/precompiles/cosmwasm-precompiles/cosmwasm" ] - } + }, + "evm/precompiles/auth", + "evm/precompiles/authz", + "evm/precompiles/evidence", + "evm/precompiles/feegrant", + "evm/precompiles/mint", + "evm/precompiles/params", + "evm/precompiles/slashing", + "evm/precompiles/upgrade" ] } ] @@ -1714,4 +1722,4 @@ "icons": { "library": "fontawesome" } -} \ No newline at end of file +} diff --git a/evm/precompiles/auth.mdx b/evm/precompiles/auth.mdx new file mode 100644 index 0000000..86c7b6b --- /dev/null +++ b/evm/precompiles/auth.mdx @@ -0,0 +1,111 @@ +--- +title: Auth Precompile +description: Query account information, auth module parameters, and the next account number from the EVM. +--- + +The Auth precompile exposes read-only access to the Cosmos SDK `auth` module, letting Solidity contracts look up account details, the auth module parameters, and the next account number. All of its methods are views and never mutate state. + +## Address + +The Auth precompile is available at the fixed address `0x000000000000000000000000000000000000100D`. + +## Solidity Interface + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant AUTH_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000100D; + +IAuth constant AUTH_CONTRACT = IAuth(AUTH_PRECOMPILE_ADDRESS); + +interface IAuth { + // Queries + + /** + * @notice Get account information for the given address + * @param addr The EVM address (must be associated with a Sei address) + * @return account Account details + */ + function account( + address addr + ) external view returns (Account memory account); + + /** + * @notice Get all accounts, paginated + * @param pageKey Pagination key (empty bytes for the first page) + * @return response Accounts response with pagination + */ + function accounts( + bytes memory pageKey + ) external view returns (AccountsResponse memory response); + + /** + * @notice Get the auth module parameters + * @return params Auth module parameters + */ + function params() external view returns (AuthParams memory params); + + /** + * @notice Get the next account number + * @return count The next account number + */ + function nextAccountNumber() external view returns (uint64 count); + + // Structs + + struct Account { + string accountAddress; + uint64 accountNumber; + uint64 sequence; + } + + struct AccountsResponse { + Account[] accounts; + bytes nextKey; + } + + struct AuthParams { + uint64 maxMemoCharacters; + uint64 txSigLimit; + uint64 txSizeCostPerByte; + uint64 sigVerifyCostEd25519; + uint64 sigVerifyCostSecp256k1; + bool disableSeqnoCheck; + } +} +``` + +## Methods + +### account + +```solidity +function account(address addr) external view returns (Account memory account); +``` + +Returns the `Account` details (address, account number, and sequence) for the Sei address associated with `addr`. The EVM address must be associated with a Sei address; if it is not, or if no account exists for the resolved address, the call reverts. + +### accounts + +```solidity +function accounts(bytes memory pageKey) external view returns (AccountsResponse memory response); +``` + +Returns a paginated list of all accounts. Pass empty `bytes` for the first page, then supply the returned `nextKey` to fetch subsequent pages. + +### params + +```solidity +function params() external view returns (AuthParams memory params); +``` + +Returns the auth module parameters, including `maxMemoCharacters`, `txSigLimit`, `txSizeCostPerByte`, `sigVerifyCostEd25519`, `sigVerifyCostSecp256k1`, and `disableSeqnoCheck`. + +### nextAccountNumber + +```solidity +function nextAccountNumber() external view returns (uint64 count); +``` + +Returns the next account number. This is a read-only view and does not increment the persisted global account number counter, so repeated calls return the same value. diff --git a/evm/precompiles/authz.mdx b/evm/precompiles/authz.mdx new file mode 100644 index 0000000..8c659cf --- /dev/null +++ b/evm/precompiles/authz.mdx @@ -0,0 +1,151 @@ +--- +title: Authz Precompile +description: Query authorization grants from the Cosmos authz module directly from EVM smart contracts on Sei. +--- + +The Authz precompile exposes read-only queries from the Cosmos `authz` module to EVM smart contracts. It lets contracts inspect authorization grants between a granter and a grantee, as well as all grants issued or received by a given account. + +## Address + +The Authz precompile is available at the fixed address: + +``` +0x000000000000000000000000000000000000100E +``` + +## Interface + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant AUTHZ_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000100e; + +IAuthz constant AUTHZ_CONTRACT = IAuthz(AUTHZ_PRECOMPILE_ADDRESS); + +interface IAuthz { + // Queries + + /** + * @notice Get grants between a granter and a grantee, optionally filtered by message type URL + * @param granter The granter's EVM address (must be associated with a Sei address) + * @param grantee The grantee's EVM address (must be associated with a Sei address) + * @param msgTypeUrl The message type URL to filter by (empty string for all) + * @param pageKey Pagination key (empty bytes for the first page) + * @return response Grants response with pagination + */ + function grants( + address granter, + address grantee, + string memory msgTypeUrl, + bytes memory pageKey + ) external view returns (GrantsResponse memory response); + + /** + * @notice Get all grants granted by a granter + * @param granter The granter's EVM address (must be associated with a Sei address) + * @param pageKey Pagination key (empty bytes for the first page) + * @return response Grant authorizations response with pagination + */ + function granterGrants( + address granter, + bytes memory pageKey + ) external view returns (GrantAuthorizationsResponse memory response); + + /** + * @notice Get all grants granted to a grantee + * @param grantee The grantee's EVM address (must be associated with a Sei address) + * @param pageKey Pagination key (empty bytes for the first page) + * @return response Grant authorizations response with pagination + */ + function granteeGrants( + address grantee, + bytes memory pageKey + ) external view returns (GrantAuthorizationsResponse memory response); + + // Structs + + struct Grant { + bytes authorization; + int64 expiration; + } + + struct GrantsResponse { + Grant[] grants; + bytes nextKey; + } + + struct GrantAuthorization { + string granter; + string grantee; + bytes authorization; + int64 expiration; + } + + struct GrantAuthorizationsResponse { + GrantAuthorization[] grants; + bytes nextKey; + } +} +``` + +## Methods + +All methods on this precompile are read-only `view` queries. They do not mutate state. + +### grants + +Returns the grants between a specific granter and grantee, optionally filtered by a message type URL. + +```solidity +function grants( + address granter, + address grantee, + string memory msgTypeUrl, + bytes memory pageKey +) external view returns (GrantsResponse memory response); +``` + +- `granter` — the granter's EVM address, which must be associated with a Sei address. +- `grantee` — the grantee's EVM address, which must be associated with a Sei address. +- `msgTypeUrl` — the message type URL to filter by. Pass an empty string to return all grants. +- `pageKey` — the pagination key; pass empty bytes to request the first page. + +Each returned `Grant` contains the `authorization` encoded as JSON bytes and an `expiration` expressed as a Unix timestamp in seconds. The `nextKey` field of the response is the pagination key for the next page (empty when the result set is exhausted). + +### granterGrants + +Returns every grant issued by a given granter. + +```solidity +function granterGrants( + address granter, + bytes memory pageKey +) external view returns (GrantAuthorizationsResponse memory response); +``` + +- `granter` — the granter's EVM address, which must be associated with a Sei address. +- `pageKey` — the pagination key; pass empty bytes to request the first page. + +Each returned `GrantAuthorization` includes the bech32 `granter` and `grantee` addresses, the `authorization` encoded as JSON bytes, and an `expiration` as a Unix timestamp in seconds. + +### granteeGrants + +Returns every grant received by a given grantee. + +```solidity +function granteeGrants( + address grantee, + bytes memory pageKey +) external view returns (GrantAuthorizationsResponse memory response); +``` + +- `grantee` — the grantee's EVM address, which must be associated with a Sei address. +- `pageKey` — the pagination key; pass empty bytes to request the first page. + +The response mirrors `granterGrants`, listing each `GrantAuthorization` with its granter, grantee, JSON-encoded authorization, and expiration. + +## Notes + +- The addresses passed to these methods must be associated with a Sei address; querying an unassociated address reverts. +- The `authorization` field is returned as JSON-encoded bytes and includes an `@type` discriminator (for example, a `SendAuthorization`). diff --git a/evm/precompiles/cosmwasm-precompiles/addr.mdx b/evm/precompiles/cosmwasm-precompiles/addr.mdx index 13ba17b..5f527d5 100644 --- a/evm/precompiles/cosmwasm-precompiles/addr.mdx +++ b/evm/precompiles/cosmwasm-precompiles/addr.mdx @@ -65,6 +65,8 @@ function associatePubKey( ) external returns (string memory seiAddr, address evmAddr); ``` + Both `associate()` and `associatePubKey()` are classified as transaction (state-mutating) methods on the precompile — the precompile's `IsTransaction` returns true for each of them — so each must be sent as a transaction rather than called as a read-only view. + ### Query functions ```solidity diff --git a/evm/precompiles/cosmwasm-precompiles/bank.mdx b/evm/precompiles/cosmwasm-precompiles/bank.mdx index 931ac12..7d5476d 100644 --- a/evm/precompiles/cosmwasm-precompiles/bank.mdx +++ b/evm/precompiles/cosmwasm-precompiles/bank.mdx @@ -30,6 +30,96 @@ function sendNative( The examples below use `balance()` with `usei`. They do not cover arbitrary Bank Module denominations. + +## Read-only query methods + +The Bank precompile also exposes a set of read-only query methods that mirror the Bank Module's gRPC queries. + +```solidity +/// Returns the spendable (unlocked) balances of an account, paginated. +function spendableBalances( + address acc, + bytes memory pageKey +) external view returns (Coin[] memory balances, bytes memory nextKey); + +/// Returns the total supply of a single denomination. +function supply( + string memory denom +) external view returns (uint256 response); + +/// Returns the total supply of every denomination, paginated. +function totalSupply( + bytes memory pageKey +) external view returns (Coin[] memory supply, bytes memory nextKey); + +/// Returns the Bank Module parameters. +function params() external view returns (Params memory params); + +/// Returns the metadata for a single denomination. +function denomMetadata( + string memory denom +) external view returns (Metadata memory metadata); + +/// Returns the metadata for every denomination, paginated. +function denomsMetadata( + bytes memory pageKey +) external view returns (Metadata[] memory metadatas, bytes memory nextKey); +``` + +`supply` and `totalSupply` are distinct: `supply(denom)` returns the total supply of a single denomination as a `uint256`, while `totalSupply(pageKey)` returns a paginated list of every denomination's supply. + +The supporting structs are: + +```solidity +struct Coin { + uint256 amount; + string denom; +} + +struct SendEnabled { + string denom; + bool enabled; +} + +struct Params { + SendEnabled[] sendEnabled; + bool defaultSendEnabled; +} + +struct DenomUnit { + string denom; + uint32 exponent; + string[] aliases; +} + +struct Metadata { + string description; + DenomUnit[] denomUnits; + string base; + string display; + string name; + string symbol; +} +``` + +Methods that accept a `pageKey` argument are paginated. Pass empty bytes (`0x`) to request the first page, then pass the returned `nextKey` to fetch the next page. An empty `nextKey` means there are no more pages. + +```typescript +const account = '0x1234567890123456789012345678901234567890'; + +const [balances, nextKey] = await readBank.spendableBalances(account, '0x'); +for (const coin of balances) { + console.log(`${coin.amount} ${coin.denom}`); +} + +const seiSupply = await readBank.supply('usei'); +console.log(`${ethers.formatUnits(seiSupply, 6)} SEI total supply`); + +const metadata = await readBank.denomMetadata('usei'); +console.log(metadata.name, metadata.symbol); +``` + + ## Setup ```bash diff --git a/evm/precompiles/evidence.mdx b/evm/precompiles/evidence.mdx new file mode 100644 index 0000000..48e4cb3 --- /dev/null +++ b/evm/precompiles/evidence.mdx @@ -0,0 +1,85 @@ +--- +title: Evidence Precompile +description: Query the evidence module from the EVM using the Evidence precompile. +--- + +The Evidence precompile exposes the Cosmos `evidence` module's read-only queries to EVM smart contracts. It lets you look up a single piece of evidence by its hash or page through all recorded evidence. Both methods are views and never mutate state. + +## Address + +The Evidence precompile is located at the following address: + +``` +0x000000000000000000000000000000000000100F +``` + +## Interface + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant EVIDENCE_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000100f; + +IEvidence constant EVIDENCE_CONTRACT = IEvidence(EVIDENCE_PRECOMPILE_ADDRESS); + +interface IEvidence { + // Queries + // Returns the JSON encoding of the evidence with the given hash. + function evidence(bytes memory evidenceHash) external view returns (bytes memory); + + // Returns all evidence, each entry JSON-encoded, with pagination. + function allEvidence(bytes memory pageKey) external view returns (AllEvidenceResponse memory response); + + // Structs + struct AllEvidenceResponse { + bytes[] evidenceList; + bytes nextKey; + } +} +``` + +## Methods + +### `evidence` + +Returns the evidence recorded under the given hash, JSON-encoded as bytes. The call reverts if no evidence exists for the provided hash. + +```solidity +function evidence(bytes memory evidenceHash) external view returns (bytes memory); +``` + +**Parameters** + +- `evidenceHash` — the hash of the evidence to look up. + +**Returns** + +- A `bytes` value containing the JSON encoding of the evidence (for example an `Equivocation` record with its `@type`, height, power, time and consensus address fields). + +### `allEvidence` + +Returns all recorded evidence, paginated. Each entry in `evidenceList` is the JSON encoding of a single piece of evidence. + +```solidity +function allEvidence(bytes memory pageKey) external view returns (AllEvidenceResponse memory response); +``` + +**Parameters** + +- `pageKey` — the pagination key returned by a previous call. Pass empty bytes (`""`) to request the first page. + +**Returns** + +- `AllEvidenceResponse` containing: + - `evidenceList` — an array of JSON-encoded evidence entries. + - `nextKey` — the pagination key for the next page, or empty bytes when the results are exhausted. + +## Structs + +### `AllEvidenceResponse` + +| Field | Type | Description | +| -------------- | --------- | ------------------------------------------------------ | +| `evidenceList` | `bytes[]` | JSON-encoded evidence entries for the current page. | +| `nextKey` | `bytes` | Pagination key for the next page (empty when exhausted).| diff --git a/evm/precompiles/example-usage.mdx b/evm/precompiles/example-usage.mdx index f182f51..70d6cdd 100644 --- a/evm/precompiles/example-usage.mdx +++ b/evm/precompiles/example-usage.mdx @@ -22,7 +22,15 @@ Sei precompiles are special smart contracts deployed at fixed addresses. They ex | Pointer view | `0x000000000000000000000000000000000000100A` | Look up pointer contract addresses | | Pointer | `0x000000000000000000000000000000000000100B` | Register pointer contracts | | Solo | `0x000000000000000000000000000000000000100C` | Claim Solo migration payloads | +| Auth | `0x000000000000000000000000000000000000100D` | Query accounts, account params, and the next account number | +| Authz | `0x000000000000000000000000000000000000100E` | Query authz grants by granter and grantee | +| Evidence | `0x000000000000000000000000000000000000100F` | Query misbehaviour evidence | +| Feegrant | `0x0000000000000000000000000000000000001010` | Query fee allowances | | P256 | `0x0000000000000000000000000000000000001011` | Verify P-256 elliptic curve signatures | +| Mint | `0x0000000000000000000000000000000000001012` | Query the mint params and minter state | +| Params | `0x0000000000000000000000000000000000001013` | Query module params by subspace and key | +| Slashing | `0x0000000000000000000000000000000000001014` | Query slashing params and validator signing info | +| Upgrade | `0x0000000000000000000000000000000000001015` | Query the current/applied upgrade plan and module versions | This page shows Bank, Staking, Governance, Distribution, JSON, and Solo. The other precompiles have their own pages: [CosmWasm](/evm/precompiles/cosmwasm-precompiles/cosmwasm), [Address](/evm/precompiles/cosmwasm-precompiles/addr), [Pointer contracts](/evm/evm-parity/examples/pointer-contracts) for both pointer precompiles, and [P256](/evm/precompiles/p256-precompile). diff --git a/evm/precompiles/feegrant.mdx b/evm/precompiles/feegrant.mdx new file mode 100644 index 0000000..018a5c0 --- /dev/null +++ b/evm/precompiles/feegrant.mdx @@ -0,0 +1,96 @@ +--- +title: Feegrant Precompile +description: Query fee allowances granted on Sei directly from EVM smart contracts using the Feegrant precompile. +--- + +The Feegrant precompile exposes read-only queries over the Cosmos `feegrant` module, letting EVM contracts inspect the fee allowances that granters have issued to grantees. + +## Address + +The Feegrant precompile is located at the fixed address: + +``` +0x0000000000000000000000000000000000001010 +``` + +## Interface + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant FEEGRANT_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001010; + +IFeegrant constant FEEGRANT_CONTRACT = IFeegrant(FEEGRANT_PRECOMPILE_ADDRESS); + +interface IFeegrant { + // Queries + // Returns the fee allowance granted to the grantee by the granter. The + // allowance field of the returned grant is JSON-encoded. + function allowance(address granter, address grantee) external view returns (Grant memory grant); + + // Returns all the fee allowances granted to the given grantee, with pagination. + function allowances(address grantee, bytes memory pageKey) external view returns (AllowancesResponse memory response); + + // Returns all the fee allowances issued by the given granter, with pagination. + function allowancesByGranter(address granter, bytes memory pageKey) external view returns (AllowancesResponse memory response); + + // Structs + struct Grant { + string granter; + string grantee; + bytes allowance; + } + + struct AllowancesResponse { + Grant[] allowances; + bytes nextKey; + } +} +``` + +## Methods + +### `allowance` + +Returns the fee allowance granted to a grantee by a granter. + +```solidity +function allowance(address granter, address grantee) external view returns (Grant memory grant); +``` + +- `granter` — the granter's EVM address (must be associated with a Sei address). +- `grantee` — the grantee's EVM address (must be associated with a Sei address). + +The returned `Grant` contains the bech32 `granter` and `grantee` addresses, and the `allowance` field holds the grant's allowance encoded as JSON. The call reverts if no allowance exists between the two addresses, or if either address is not associated with a Sei address. + +### `allowances` + +Returns all fee allowances granted to the given grantee, with pagination. + +```solidity +function allowances(address grantee, bytes memory pageKey) external view returns (AllowancesResponse memory response); +``` + +- `grantee` — the grantee's EVM address (must be associated with a Sei address). +- `pageKey` — the pagination key from a previous response; pass empty bytes for the first page. + +The returned `AllowancesResponse` contains the list of `Grant` entries and a `nextKey` used to fetch the next page (empty when the results are exhausted). + +### `allowancesByGranter` + +Returns all fee allowances issued by the given granter, with pagination. + +```solidity +function allowancesByGranter(address granter, bytes memory pageKey) external view returns (AllowancesResponse memory response); +``` + +- `granter` — the granter's EVM address (must be associated with a Sei address). +- `pageKey` — the pagination key from a previous response; pass empty bytes for the first page. + +The returned `AllowancesResponse` follows the same shape as `allowances`. + +## Notes + +- All methods are view-only queries and must not be called with a non-zero `value`; doing so reverts. +- The `allowance` field of each `Grant` is JSON-encoded and includes a `@type` field identifying the allowance type (for example `BasicAllowance`). diff --git a/evm/precompiles/governance.mdx b/evm/precompiles/governance.mdx index 7ad3426..cb3aaca 100644 --- a/evm/precompiles/governance.mdx +++ b/evm/precompiles/governance.mdx @@ -101,6 +101,114 @@ function submitProposal( ) payable external returns (uint64 proposalID); ``` + +### Query functions + +The governance precompile also exposes read-only query methods so you can read proposal state on-chain instead of relying on Cosmos REST or RPC. The vote and deposit queries are named `getVote` and `getDeposit` (rather than overloading the `vote`/`deposit` transaction methods) because ambiguous function names break common tooling such as ethers.js. + +```solidity +struct Coin { + uint256 amount; + string denom; +} + +struct TallyResultData { + string yes; + string abstain; + string no; + string noWithVeto; +} + +struct WeightedVoteOptionData { + int32 option; + string weight; // Weight as decimal string (e.g., "0.7") +} + +struct ProposalData { + uint64 id; + int32 status; // ProposalStatus enum value + TallyResultData finalTallyResult; + int64 submitTime; // Unix seconds + int64 depositEndTime; // Unix seconds + Coin[] totalDeposit; + int64 votingStartTime; // Unix seconds + int64 votingEndTime; // Unix seconds + bool isExpedited; + bytes content; // proposal content as JSON +} + +struct VoteData { + uint64 proposalId; + string voter; // bech32 address + WeightedVoteOptionData[] options; +} + +struct DepositData { + uint64 proposalId; + string depositor; // bech32 address + Coin[] amount; +} + +struct GovParams { + uint64 votingPeriod; // seconds + uint64 expeditedVotingPeriod; // seconds + Coin[] minDeposit; + uint64 maxDepositPeriod; // seconds + Coin[] minExpeditedDeposit; + string quorum; + string threshold; + string vetoThreshold; + string expeditedQuorum; + string expeditedThreshold; +} + +/// Query a proposal by ID +function proposal( + uint64 proposalID +) external view returns (ProposalData memory proposal); + +/// Query proposals with optional filters. proposalStatus 0 = all; a zero +/// address for voter/depositor applies no filter. +function proposals( + int32 proposalStatus, + address voter, + address depositor, + bytes memory pageKey +) external view returns (ProposalData[] memory proposals, bytes memory nextKey); + +/// Query a vote cast on a proposal +function getVote( + uint64 proposalID, + address voter +) external view returns (VoteData memory vote); + +/// Query all votes cast on a proposal, paginated +function votes( + uint64 proposalID, + bytes memory pageKey +) external view returns (VoteData[] memory votes, bytes memory nextKey); + +/// Query the governance module parameters +function params() external view returns (GovParams memory params); + +/// Query a deposit made to a proposal +function getDeposit( + uint64 proposalID, + address depositor +) external view returns (DepositData memory deposit); + +/// Query all deposits made to a proposal, paginated +function deposits( + uint64 proposalID, + bytes memory pageKey +) external view returns (DepositData[] memory deposits, bytes memory nextKey); + +/// Query the current tally of votes on a proposal +function tallyResult( + uint64 proposalID +) external view returns (TallyResultData memory tallyResult); +``` + ## Using the contract ### Setup diff --git a/evm/precompiles/mint.mdx b/evm/precompiles/mint.mdx new file mode 100644 index 0000000..e5e6d40 --- /dev/null +++ b/evm/precompiles/mint.mdx @@ -0,0 +1,113 @@ +--- +title: Mint Precompile +description: Query the Sei mint module from EVM smart contracts to read inflation parameters and the current minter state. +--- + +The Mint precompile exposes read-only queries against the Sei mint module, letting EVM smart contracts inspect the chain's token release schedule and the current minter state. + +## Address + +The Mint precompile is located at the following address: + +``` +0x0000000000000000000000000000000000001012 +``` + +## Interface + +```solidity Mint.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant MINT_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001012; + +IMint constant MINT_CONTRACT = IMint(MINT_PRECOMPILE_ADDRESS); + +interface IMint { + // Queries + function params() external view returns (MintParams memory); + function minter() external view returns (Minter memory); + + // Structs + struct ScheduledTokenRelease { + string startDate; + string endDate; + uint64 tokenReleaseAmount; + } + + struct MintParams { + string mintDenom; + ScheduledTokenRelease[] tokenReleaseSchedule; + } + + struct Minter { + string startDate; + string endDate; + string denom; + uint64 totalMintAmount; + uint64 remainingMintAmount; + uint64 lastMintAmount; + string lastMintDate; + uint64 lastMintHeight; + } +} +``` + +## Methods + +All methods on the Mint precompile are read-only `view` functions. Calling them with a non-zero `value` reverts. + +### `params` + +Returns the mint module parameters, including the mint denom and the full scheduled token release schedule. + +```solidity +function params() external view returns (MintParams memory); +``` + +**Returns** + +- `MintParams` — a struct containing: + - `mintDenom` — the denomination minted by the module. + - `tokenReleaseSchedule` — an array of `ScheduledTokenRelease` entries, each with a `startDate`, `endDate`, and `tokenReleaseAmount`. + +### `minter` + +Returns the current minter state, describing the active release window and the amounts minted so far. + +```solidity +function minter() external view returns (Minter memory); +``` + +**Returns** + +- `Minter` — a struct containing: + - `startDate` — the start date of the current release period. + - `endDate` — the end date of the current release period. + - `denom` — the denomination being minted. + - `totalMintAmount` — the total amount to be minted over the current period. + - `remainingMintAmount` — the amount still to be minted in the current period. + - `lastMintAmount` — the amount minted in the most recent mint. + - `lastMintDate` — the date of the most recent mint. + - `lastMintHeight` — the block height of the most recent mint. + +## Usage Example + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./Mint.sol"; + +contract MintReader { + function getMintDenom() external view returns (string memory) { + IMint.MintParams memory params = MINT_CONTRACT.params(); + return params.mintDenom; + } + + function getRemainingToMint() external view returns (uint64) { + IMint.Minter memory minter = MINT_CONTRACT.minter(); + return minter.remainingMintAmount; + } +} +``` diff --git a/evm/precompiles/params.mdx b/evm/precompiles/params.mdx new file mode 100644 index 0000000..305a3c7 --- /dev/null +++ b/evm/precompiles/params.mdx @@ -0,0 +1,69 @@ +--- +title: Params Precompile +description: Read on-chain module parameters from any x/params subspace through the Params precompile. +--- + +The Params precompile exposes the Cosmos SDK `x/params` subspaces to the EVM, letting contracts read arbitrary module parameters by subspace and key. It is a read-only precompile deployed at a fixed address. + +## Address + +The Params precompile is available at the following address: + +``` +0x0000000000000000000000000000000000001013 +``` + +## Interface + +```solidity Params.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant PARAMS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001013; + +IParams constant PARAMS_CONTRACT = IParams(PARAMS_PRECOMPILE_ADDRESS); + +interface IParams { + // Queries + function params(string memory subspace, string memory key) external view returns (string memory value); +} +``` + +## Methods + +### params + +Returns the value of the parameter identified by `subspace` and `key`. The value is returned as its JSON-encoded string representation. + +```solidity +function params( + string memory subspace, + string memory key +) external view returns (string memory value); +``` + +**Parameters** + +- `subspace` — the name of the params subspace to read from (e.g. `"staking"`). +- `key` — the parameter key within the subspace (e.g. `"MaxValidators"`). + +**Returns** + +- `value` — the JSON-encoded value of the requested parameter. + +The call reverts if the subspace or key is unknown or empty. + +## Example Usage + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IParams, PARAMS_CONTRACT} from "./Params.sol"; + +contract ParamsReader { + function getMaxValidators() external view returns (string memory) { + return PARAMS_CONTRACT.params("staking", "MaxValidators"); + } +} +``` diff --git a/evm/precompiles/slashing.mdx b/evm/precompiles/slashing.mdx new file mode 100644 index 0000000..2ba8739 --- /dev/null +++ b/evm/precompiles/slashing.mdx @@ -0,0 +1,158 @@ +--- +title: "Slashing Precompile" +description: "Query the slashing module's parameters and validator signing information directly from the EVM." +--- + +The Slashing precompile exposes the Cosmos `slashing` module's read-only queries to EVM smart contracts. It lets you inspect the module parameters that govern downtime and double-sign penalties, and read per-validator signing information such as missed block counts and jailing status. + +## Address + +The Slashing precompile is available at the fixed address: + +``` +0x0000000000000000000000000000000000001014 +``` + +## Interface + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant SLASHING_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001014; + +ISlashing constant SLASHING_CONTRACT = ISlashing(SLASHING_PRECOMPILE_ADDRESS); + +interface ISlashing { + // Queries + function params() external view returns (SlashingParams memory params); + + function signingInfo( + string memory consAddress + ) external view returns (SigningInfo memory signingInfo); + + function signingInfos( + bytes memory pageKey + ) external view returns (SigningInfosResponse memory response); + + // Structs + struct SlashingParams { + int64 signedBlocksWindow; + string minSignedPerWindow; + uint64 downtimeJailDuration; + string slashFractionDoubleSign; + string slashFractionDowntime; + } + + struct SigningInfo { + string validatorAddress; + int64 startHeight; + int64 indexOffset; + int64 jailedUntil; + bool tombstoned; + int64 missedBlocksCounter; + } + + struct SigningInfosResponse { + SigningInfo[] signingInfos; + bytes nextKey; + } +} +``` + +## Methods + +### `params` + +```solidity +function params() external view returns (SlashingParams memory params); +``` + +Returns the slashing module parameters. + +**Returns** a `SlashingParams` struct: + +| Field | Type | Description | +| ------------------------- | -------- | --------------------------------------------------------------------------- | +| `signedBlocksWindow` | `int64` | The size of the sliding window used to track validator liveness. | +| `minSignedPerWindow` | `string` | Minimum fraction of blocks a validator must sign per window (decimal string).| +| `downtimeJailDuration` | `uint64` | Duration, in seconds, a validator is jailed for downtime. | +| `slashFractionDoubleSign` | `string` | Fraction of stake slashed for a double-sign (decimal string). | +| `slashFractionDowntime` | `string` | Fraction of stake slashed for downtime (decimal string). | + +### `signingInfo` + +```solidity +function signingInfo( + string memory consAddress +) external view returns (SigningInfo memory signingInfo); +``` + +Returns the signing information for a single validator identified by its consensus address (bech32). + +**Parameters** + +| Parameter | Type | Description | +| ------------- | -------- | ----------------------------------------- | +| `consAddress` | `string` | The validator's consensus address (bech32).| + +**Returns** a `SigningInfo` struct: + +| Field | Type | Description | +| --------------------- | -------- | ---------------------------------------------------------------- | +| `validatorAddress` | `string` | The validator's consensus address (bech32). | +| `startHeight` | `int64` | Height at which the validator became bonded. | +| `indexOffset` | `int64` | Index offset into the signed block bit array. | +| `jailedUntil` | `int64` | Unix timestamp (seconds) until which the validator is jailed. | +| `tombstoned` | `bool` | Whether the validator has been tombstoned. | +| `missedBlocksCounter` | `int64` | Number of blocks the validator has missed within the window. | + +Querying a consensus address with no signing information reverts. + +### `signingInfos` + +```solidity +function signingInfos( + bytes memory pageKey +) external view returns (SigningInfosResponse memory response); +``` + +Returns the signing information for all validators, with pagination. + +**Parameters** + +| Parameter | Type | Description | +| --------- | ------- | ---------------------------------------------------------------- | +| `pageKey` | `bytes` | Pagination key from a previous response; pass empty bytes for the first page.| + +**Returns** a `SigningInfosResponse` struct: + +| Field | Type | Description | +| -------------- | --------------- | ------------------------------------------------------- | +| `signingInfos` | `SigningInfo[]` | The signing information entries for the current page. | +| `nextKey` | `bytes` | Pagination key for the next page; empty when exhausted. | + +## Example Usage + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./Slashing.sol"; + +contract SlashingReader { + function getDowntimeSlashFraction() external view returns (string memory) { + ISlashing.SlashingParams memory params = SLASHING_CONTRACT.params(); + return params.slashFractionDowntime; + } + + function getMissedBlocks(string memory consAddress) external view returns (int64) { + ISlashing.SigningInfo memory info = SLASHING_CONTRACT.signingInfo(consAddress); + return info.missedBlocksCounter; + } +} +``` + + + All methods on the Slashing precompile are read-only `view` queries. Sending value to any of them reverts. + diff --git a/evm/precompiles/upgrade.mdx b/evm/precompiles/upgrade.mdx new file mode 100644 index 0000000..6ef559c --- /dev/null +++ b/evm/precompiles/upgrade.mdx @@ -0,0 +1,132 @@ +--- +title: Upgrade Precompile +description: Query the chain upgrade module from EVM smart contracts using the Upgrade precompile. +--- + +The Upgrade precompile exposes read-only queries against the chain's upgrade module to EVM smart contracts. It lets contracts inspect the currently scheduled upgrade plan, look up when a named upgrade was applied, read stored upgraded consensus state, and read the consensus versions of app modules. + +## Precompile Address + +The Upgrade precompile is available at the fixed address: + +``` +0x0000000000000000000000000000000000001015 +``` + +## Solidity Interface + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant UPGRADE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001015; + +IUpgrade constant UPGRADE_CONTRACT = IUpgrade(UPGRADE_PRECOMPILE_ADDRESS); + +interface IUpgrade { + // Queries + // Returns the currently scheduled upgrade plan, or a zero-valued plan if + // no upgrade is scheduled. + function currentPlan() external view returns (Plan memory plan); + + // Returns the block height at which the named upgrade was applied, or 0 + // if it has not been applied. + function appliedPlan( + string memory name + ) external view returns (int64 height); + + // Returns the upgraded consensus state stored for the given last height, + // or empty bytes if none is stored. + function upgradedConsensusState( + int64 lastHeight + ) external view returns (bytes memory consensusState); + + // Returns the consensus versions of app modules. An empty moduleName + // returns all modules; a specific moduleName returns just that module. + function moduleVersions( + string memory moduleName + ) external view returns (ModuleVersion[] memory versions); + + // Structs + struct Plan { + string name; + int64 height; + string info; + } + + struct ModuleVersion { + string name; + uint64 version; + } +} +``` + +## Methods + +All methods are read-only `view` queries and are non-payable; sending value reverts the call. + +### currentPlan + +```solidity +function currentPlan() external view returns (Plan memory plan); +``` + +Returns the currently scheduled upgrade plan. If no upgrade is scheduled, a zero-valued `Plan` is returned (empty `name` and `info`, `height` of `0`). + +The returned `Plan` contains: + +- `name` — the name of the scheduled upgrade. +- `height` — the block height at which the upgrade is scheduled to run. +- `info` — arbitrary metadata attached to the plan. + +### appliedPlan + +```solidity +function appliedPlan(string memory name) external view returns (int64 height); +``` + +Returns the block height at which the upgrade identified by `name` was applied. If the upgrade has not been applied, it returns `0`. + +### upgradedConsensusState + +```solidity +function upgradedConsensusState(int64 lastHeight) external view returns (bytes memory consensusState); +``` + +Returns the upgraded consensus state stored for the given `lastHeight`. If no consensus state is stored for that height, it returns empty bytes. + +### moduleVersions + +```solidity +function moduleVersions(string memory moduleName) external view returns (ModuleVersion[] memory versions); +``` + +Returns the consensus versions of app modules. Passing an empty `moduleName` returns every module; passing a specific `moduleName` returns a single entry for that module. Querying an unknown module name reverts. + +Each `ModuleVersion` contains: + +- `name` — the module's name. +- `version` — the module's consensus version. + +## Example Usage + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IUpgrade, UPGRADE_CONTRACT} from "./Upgrade.sol"; + +contract UpgradeReader { + function getCurrentPlan() external view returns (IUpgrade.Plan memory) { + return UPGRADE_CONTRACT.currentPlan(); + } + + function getAppliedHeight(string memory name) external view returns (int64) { + return UPGRADE_CONTRACT.appliedPlan(name); + } + + function getModuleVersions() external view returns (IUpgrade.ModuleVersion[] memory) { + return UPGRADE_CONTRACT.moduleVersions(""); + } +} +``` From 9fe9770012c634bb58a3dd8ea2ecee29958ffa8d Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:13:01 +0000 Subject: [PATCH 32/43] docs: The EVM module now tracks recent block hashes in a KV store ring (last 256 blocks) plus a process-local cache, making the BLOCKHASH opcode return correct historical block hashes for the previous 256 blocks. (sei-protocol/sei-chain#3811) --- evm/differences-with-ethereum.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evm/differences-with-ethereum.mdx b/evm/differences-with-ethereum.mdx index 2ac584c..aa376a1 100644 --- a/evm/differences-with-ethereum.mdx +++ b/evm/differences-with-ethereum.mdx @@ -101,6 +101,10 @@ state root, which is an MPT root. Sei computes the block hash from the block header in Tendermint data format. As a result, it is different from Ethereum's block hash. +The `BLOCKHASH` opcode returns the correct historical block hash for the previous 256 blocks, matching the EVM Yellow Paper's availability window. Sei maintains an on-chain ring of recent block hashes that is populated during `BeginBlock` from the parent block's hash, backed by a process-local cache, with a fallback to staking historical info. Before this change, `BLOCKHASH` for recent blocks (such as `height - 1`) returned an empty hash. Hashes outside the 256-block window, or hashes of future/current-height blocks, return zero, and `BLOCKHASH` values remain non-interchangeable with Ethereum because of the different header encoding. + +{/* For additional context, see the opcode differences table above. */} + ## Base fee & tips Sei supports all non‑blob transaction types, including the Pectra `SetCode` transaction (EIP‑7702). However, for a legacy (non EIP‑1559) From 6d0b2c2a572ca4d9d79f8731ab8302d4db6218e1 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:13:59 +0000 Subject: [PATCH 33/43] docs: Adds a new `max_log_bytes` EVM config field and changes `max_log_no_block` behavior so `eth_getLogs` now errors (instead of silently truncating) when a bounded or open-ended query exceeds the matched-log count or estimated heap-byte budget. (sei-protocol/sei-chain#3759) --- evm/evm-parity/evm-compatibility.mdx | 2 +- evm/reference.mdx | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/evm/evm-parity/evm-compatibility.mdx b/evm/evm-parity/evm-compatibility.mdx index 3688eb9..a5c8f47 100644 --- a/evm/evm-parity/evm-compatibility.mdx +++ b/evm/evm-parity/evm-compatibility.mdx @@ -52,7 +52,7 @@ Standard EVM tools (viem, wagmi, ethers, Foundry, and Hardhat) work on Sei for a | Feature | Sei status | Notes | | --- | --- | --- | -| `eth_getLogs` | Supported | | +| `eth_getLogs` | Supported with differences | A single query may match at most `max_log_no_block` logs (default 10000) and `max_log_bytes` of estimated heap bytes (default 64 MiB). This applies to both bounded and open-ended block ranges. Exceeding either limit returns an error (`too many logs` / `too many log bytes`) advising you to narrow the block range or filter criteria, rather than silently truncating the result. [See RPC configuration.](/node/node-operators/rpc-configuration) | | Filter lifecycle (`eth_newFilter`, `eth_getFilterChanges`, `eth_getFilterLogs`, `eth_uninstallFilter`) | Supported | | | WebSocket subscriptions (`eth_subscribe`) | Supported | [See WebSocket Connections.](/evm/evm-parity/websocket) | diff --git a/evm/reference.mdx b/evm/reference.mdx index 94027d6..d104806 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -110,8 +110,11 @@ Because `FinalizeBlock` responses are not stored on disk under Autobahn, `/block By default, log filters (`eth_getLogs`, `eth_getFilterLogs`) have these limits: -- **Open-ended block range:** up to 10,000 logs in one response -- **Close-ended block range:** up to 2,000 blocks in one query +- **Matched-log count:** a single query may match up to 10,000 logs (`max_log_no_block`, `evm.max_log_no_block`, default 10000). This cap applies to both bounded and open-ended block ranges. Exceeding it returns a `too many logs` error rather than a truncated result. +- **Estimated log bytes:** a single query may materialize up to 64 MiB of estimated heap bytes of matched logs (`max_log_bytes`, `evm.max_log_bytes`, default 67108864 / 64 MiB). Exceeding it returns a `too many log bytes` error. A non-positive value falls back to the receipt-store default. +- **Close-ended block range:** up to 2,000 blocks in one query (`max_blocks_for_log`, `evm.max_blocks_for_log`, default 2000). + +When a query exceeds the matched-log count or the byte budget, the request now errors instead of silently truncating the result. Narrow the block range or the filter criteria (address and topics) and retry. Real-time subscriptions (`eth_subscribe` for `newHeads` and `logs`) are available over WebSocket only. For transport details, see [WebSocket connections](/evm/evm-parity/websocket). From 4f550bd1c2662f431a170ee3df8d6b27caab279b Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:14:27 +0000 Subject: [PATCH 34/43] docs: Dynamic-gas precompiles now charge gas for ABI-decoding calldata before decoding it, so calls that supply too little gas are rejected and gas costs for existing precompile calls have increased slightly. (sei-protocol/sei-chain#3737) --- evm/evm-parity/gas-and-fees.mdx | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/evm/evm-parity/gas-and-fees.mdx b/evm/evm-parity/gas-and-fees.mdx index 8eb79ed..e916638 100644 --- a/evm/evm-parity/gas-and-fees.mdx +++ b/evm/evm-parity/gas-and-fees.mdx @@ -92,6 +92,42 @@ const gas = await provider.estimateGas({ Estimate the absolute storage-write cost with a live `eth_estimateGas` call against a Sei RPC. A Foundry `forge test --gas-report --fork-url ` report forks the chain *state*, but it runs the standard EVM gas schedule of revm. It therefore shows `SSTORE` at the Ethereum cost (approximately 22,100) instead of the Sei cost (approximately 72,000). Use the report for relative profiling of your own logic. + +## Precompile calldata decode cost + +Sei's dynamic-gas precompiles (such as bank, wasmd, oracle, ibc, json, distribution, pointer, and addr) now charge gas for ABI-decoding a call's calldata *before* the arguments are decoded. The charge covers a length-proportional scan of the input plus the cost of copying any `string` payloads the decoder would materialize — a cost that can be significantly larger than the raw calldata length when a single string is referenced by many array or tuple slots. + +Two consequences for callers: + +- A precompile call that forwards too little gas to cover the decode is reverted before the precompile runs, instead of being decoded for free. +- Overall gas used for dynamic-gas precompile calls has increased slightly, since the decode is now metered. + +Because this charge depends on the exact shape and size of your calldata, do not hard-code gas limits for precompile calls. Always size them with a live `eth_estimateGas` call against a Sei RPC: + + + +```ts viem +const gas = await client.estimateGas({ + account, + to: precompileAddress, + data: encodedCalldata, +}); + +// Add a buffer for safety during high-activity periods +const gasWithBuffer = (gas * 120n) / 100n; // 20% buffer +``` + +```ts ethers +const gas = await provider.estimateGas({ + from: signer.address, + to: precompileAddress, + data: encodedCalldata, +}); +``` + + + + ## Summary | Behavior | Ethereum | Sei | From fa932d513172c23bcb2e5121abd4c558caac1feb Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:15:53 +0000 Subject: [PATCH 35/43] docs: Seed nodes now expose a Prometheus metrics endpoint (and fail startup on bind failure), the metrics server gains read/write/idle timeouts, and the max-open-connections config field's behavior is clarified. (sei-protocol/sei-chain#3819) --- node/advanced-config-monitoring.mdx | 42 +++++++++++++++++++++++++++++ node/node-operators.mdx | 22 +++++++++++++++ node/node-types.mdx | 2 +- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index 87d51af..e57a897 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -507,6 +507,48 @@ scrape_configs: +## Seed node Prometheus metrics + +Seed nodes now expose a Prometheus `/metrics` endpoint, so they can be scraped like any other node. Because a seed serves no RPC, this endpoint is the only observability surface it has — peer reachability and peer headroom both come from its p2p metrics (for example `tendermint_p2p_peers`). + +To enable it, set the usual instrumentation fields in `config.toml` under `[instrumentation]`: + +```toml +[instrumentation] +prometheus = true +prometheus-listen-addr = ":26660" +``` + +Every exposed series is stamped with the node's `chain_id` label, matching the behavior of full nodes. + + +For a seed node, a failure to bind the Prometheus listener (for example, the port is already in use or the address is unparseable) is **fatal**: the seed fails to start. This is deliberate — a seed's value is only realized when it is observable, so an unambiguous crash is preferable to a seed that discovers peers while blind. + +A full node behaves differently: a bind failure is **non-fatal**. The full node logs the error and continues running, because it still serves RPC as an in-band way to observe its health. + + +The `tendermint_p2p_peers` gauge is present from node startup rather than appearing only after the first refresh interval, so an alert comparing peer count against the connection cap matches immediately rather than sitting unmatched during the startup window. + +### Metrics server timeouts + +The Prometheus metrics HTTP server enforces request timeouts to bound how long a slow reader can hold a request slot: + +| Timeout | Value | +| --- | --- | +| `ReadHeaderTimeout` | 10s | +| `ReadTimeout` | 10s | +| `WriteTimeout` | 30s | +| `IdleTimeout` | 60s | + +### `max-open-connections` behavior + +The `max-open-connections` field under `[instrumentation]` is not a socket limit despite the name. It becomes promhttp's `MaxRequestsInFlight`: the maximum number of scrapes served concurrently. Requests past the limit receive an HTTP `503` rather than being queued, and a slow reader occupies a slot until it finishes or hits the `WriteTimeout`. A value of `0` means unlimited. + +Keep this value above the number of scrapers that can overlap — for example, an HA Prometheus pair, blackbox probes, and a human running `curl` — so a burst of concurrent scrapes does not cause a healthy node to report as unscrapeable. + + + + ## Cosmos SDK / Tendermint telemetry chain_id label Node telemetry automatically injects a `chain_id` global label into the Cosmos SDK / Tendermint Prometheus telemetry (the `GlobalLabels` of the `[telemetry]` configuration) based on the node's client chain ID. The label is appended only when a `chain_id` label is not already configured in `GlobalLabels`, so an explicit operator-set value always takes precedence. This is distinct from the `chain_id` constant label applied to the OpenTelemetry `sei_chain` namespace described above — it covers the separate Cosmos SDK/Tendermint telemetry path. diff --git a/node/node-operators.mdx b/node/node-operators.mdx index 19812d9..6e5c04b 100644 --- a/node/node-operators.mdx +++ b/node/node-operators.mdx @@ -789,6 +789,28 @@ everything. Second, when `evm-ss-db-directory` is unset, nodes with an existing to `/data/state_store/evm/{backend}`. + + +The `[instrumentation] max-open-connections` field in `config.toml` is not a +socket limit despite its name. It is passed to promhttp as +`MaxRequestsInFlight` and caps the number of Prometheus scrapes served +concurrently; requests past the cap receive an HTTP `503` rather than being +queued. Keep the value above the number of scrapers that can overlap — an HA +Prometheus pair, blackbox probes, and the occasional human `curl` — because a +slow reader occupies a slot until it finishes or hits the write timeout. `0` +means unlimited. + +The Prometheus metrics HTTP server enforces a read timeout of 10s, a write +timeout of 30s, and an idle timeout of 60s. These bound how long a slow reader +can hold a request slot and how long idle keep-alive connections persist. + +Seed nodes also serve Prometheus metrics (for example the +`tendermint_p2p_peers` gauge) when instrumentation is enabled. Because a seed +serves no RPC, Prometheus is its only observability surface, so a failure to +bind the metrics listener is fatal and causes seed startup to fail — unlike a +full node, which logs the error and continues serving. + + ### Cosmos SDK state pruning Separate from the SeiDB retention settings above (`min-retain-blocks`, diff --git a/node/node-types.mdx b/node/node-types.mdx index f306190..24d5b99 100644 --- a/node/node-types.mdx +++ b/node/node-types.mdx @@ -23,7 +23,7 @@ The `seid` binary uses these TCP ports. Change their settings to match your envi - `9090`: The default port for gRPC communication. This port is used for high-performance communication with the node. - `8545`: The default port for EVM HTTP RPC. This port is used for Ethereum JSON-RPC calls. It must be open if you want to interact with EVM-compatible applications. The [`frozen-rpc-router`](/node/technical-reference#frozen-rpc-router) binary also listens on `127.0.0.1:8545` by default. If you run the router on the same host as a live node, move one of them to a different port. The linked example keeps the router on `8545` and moves the two co-located nodes to `9545` and `9546`. - `8546`: The default port for EVM WebSocket RPC. EVM applications that need WebSocket connections use this port for real-time communication. -- `26660`: The default port for the Prometheus database, which you can use to monitor the environment. In the default configuration, this port is not open. +- `26660`: The default port for the Prometheus metrics endpoint, which you can use to monitor the environment. It is served only when instrumentation is enabled (`prometheus = true` under `[instrumentation]` in `config.toml`); in the default configuration it is not open. Seed nodes also serve Prometheus metrics when instrumentation is enabled — exposing p2p metrics such as `tendermint_p2p_peers` — which is their only observability surface since they serve no RPC. Note that on a seed node a failure to bind the Prometheus listener is fatal and crashes startup, whereas a full node logs the failure and continues. You can change all of these ports in `$HOME/.sei/config/config.toml` and `$HOME/.sei/config/app.toml`. From 7cc9ac2292c3af6ff52ac1a39ca8ec68dd2ebe5a Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:17:01 +0000 Subject: [PATCH 36/43] docs: The json, p256, and pointerview precompiles switched to a dynamic gas model, changing how gas is metered and charged for these on-chain precompile calls (notably p256 now uses a fixed 3450 gas verification cost per RIP-7212). (sei-protocol/sei-chain#3813) --- evm/evm-parity/gas-and-fees.mdx | 8 +++++++- evm/precompiles/json.mdx | 2 +- evm/precompiles/p256-precompile.mdx | 6 ++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/evm/evm-parity/gas-and-fees.mdx b/evm/evm-parity/gas-and-fees.mdx index e916638..1ec9109 100644 --- a/evm/evm-parity/gas-and-fees.mdx +++ b/evm/evm-parity/gas-and-fees.mdx @@ -95,7 +95,13 @@ const gas = await provider.estimateGas({ ## Precompile calldata decode cost -Sei's dynamic-gas precompiles (such as bank, wasmd, oracle, ibc, json, distribution, pointer, and addr) now charge gas for ABI-decoding a call's calldata *before* the arguments are decoded. The charge covers a length-proportional scan of the input plus the cost of copying any `string` payloads the decoder would materialize — a cost that can be significantly larger than the raw calldata length when a single string is referenced by many array or tuple slots. +Sei's dynamic-gas precompiles (such as bank, wasmd, oracle, ibc, json, distribution, pointer, pointerview, addr, and p256) now charge gas for ABI-decoding a call's calldata *before* the arguments are decoded. The charge covers a length-proportional scan of the input plus the cost of copying any `string` payloads the decoder would materialize — a cost that can be significantly larger than the raw calldata length when a single string is referenced by many array or tuple slots. + +The `json`, `p256`, and `pointerview` precompiles use this dynamic gas model as well. Their metering works as follows: + +- **`json`** — charges 100 gas per byte of the JSON payload being parsed, consumed against the call's gas meter. +- **`pointerview`** — self-meters based on the state reads performed during pointer lookups, rather than a flat charge. +- **`p256`** — charges a fixed **3,450 gas** verification cost, matching RIP-7212's `P256VERIFY`. Because this flat charge is levied outside the verification's panic recovery, a call that forwards too little gas to cover it fails the transaction with an out-of-gas error rather than reverting the call. Two consequences for callers: diff --git a/evm/precompiles/json.mdx b/evm/precompiles/json.mdx index e336b87..2f3b007 100644 --- a/evm/precompiles/json.mdx +++ b/evm/precompiles/json.mdx @@ -713,7 +713,7 @@ async function handleMissingKeys(jsonPrecompile: ethers.Contract, data: any) { - **Boolean values:** Use the integer 0 for false and 1 for true. - **Key paths:** Extract parent objects first, then parse them manually. Dot notation may not be supported. - **Arrays:** Object-keyed arrays use `extractAsBytesList()`. JSON arrays by index use `extractAsBytesFromArray()`. -- **Gas costs:** Large JSON objects need higher gas limits. +- **Gas costs:** The JSON precompile uses dynamic gas metering. It charges 100 gas per byte of the JSON payload (the first argument) against the call's gas meter, so larger JSON inputs consume proportionally more gas and need higher gas limits. - **Encoding:** Always use UTF-8 encoding with `ethers.toUtf8Bytes()`. - **Error handling:** Always implement fallback values for production applications. diff --git a/evm/precompiles/p256-precompile.mdx b/evm/precompiles/p256-precompile.mdx index 3b571ef..99b35d1 100644 --- a/evm/precompiles/p256-precompile.mdx +++ b/evm/precompiles/p256-precompile.mdx @@ -162,11 +162,13 @@ Total length: 160 bytes ## Gas costs -The precompile is much more gas efficient than Solidity implementations. The exact gas cost per byte of verified data is set to `GasCostPerByte = 300`. The results are: +The precompile is much more gas efficient than Solidity implementations. Verification is a constant-work, stateless operation, so it is priced as a flat charge against the call's gas meter rather than per byte of input, matching RIP-7212's `P256VERIFY`: -- **Total cost**: 300 × 160 = **48,000 gas** per verification +- **Total cost**: a fixed `P256VerifyGas = 3450` gas per verification - **Efficiency**: Up to 60x more efficient than pure Solidity implementations +The fixed verification cost is charged before the signature check runs. If the call is not funded for this cost, the resulting out-of-gas condition propagates and fails the transaction rather than being caught and downgraded to a reverted call. + ## Real-world use cases ### WebAuthn/passkeys authentication From d5c3b355ed38faed333870e5a4a0ce3ce68f35b8 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:17:43 +0000 Subject: [PATCH 37/43] docs: Cosmos transaction ante handlers now validate provided public keys (including recursive validation of nested multisig keys with depth and signature-count limits) before deriving addresses or persisting them, rejecting malformed pubkeys and deeply nested multisigs. (sei-protocol/sei-chain#3791) --- evm/transactions.mdx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/evm/transactions.mdx b/evm/transactions.mdx index 203c18c..e14f685 100644 --- a/evm/transactions.mdx +++ b/evm/transactions.mdx @@ -290,6 +290,25 @@ Before Sei accepts an EVM transaction, it performs strict semantic validation of - [Transaction types](/evm/evm-parity/transaction-types#access-list-and-auth-list-entry-validation) is the canonical field-level reference. It covers signature-value byte caps and zero-padding rules, access-list entries, and EIP-7702 authorization lists. - [Differences with Ethereum](/evm/differences-with-ethereum#evm-transaction-envelope-restrictions) covers envelope-level restrictions: no Cosmos wrapper fields, canonical protobuf encoding, and whole-block rejection on decode failure. + + +### Cosmos transaction public-key validation + +For Cosmos transactions, the ante handlers now validate every public key provided in a transaction before deriving addresses from it or persisting it to account state. This validation runs during both `CheckTx` and `DeliverTx`, so a transaction carrying a malformed key is rejected rather than silently storing an unusable key on the account. + +A public key is rejected when: + +- A `secp256k1` key has the wrong byte size or fails to parse as a valid curve point — returns `ErrInvalidPubKey`. +- A multisig key has a threshold of zero, or a threshold greater than the number of member keys — returns `ErrInvalidPubKey`. +- A multisig key is nested more than 5 levels deep — returns `ErrInvalidPubKey`. +- The number of member keys consumed exceeds the per-pubkey signature budget derived from the auth module's `TxSigLimit` — returns `ErrTooManySignatures`. + +The recursive work budget is kept local to each provided public key, so one multisig tree is bounded on its own without changing the aggregate `TxSigLimit` behavior applied to keys persisted to account state. + + + If you build and sign Cosmos transactions manually (for example, assembling multisig keys), ensure member keys are well formed and that multisig nesting stays within 5 levels to avoid rejection. + + ### Receipts for nonce-bumping failed transactions Some EVM transactions pass basic validation and bump the sender's nonce, but still fail during state transition. One example is a transaction whose gas limit clears the intrinsic-gas check but falls short of the EIP-7623 floor-data-gas requirement. This can occur in normal operation after Pectra. Because these transactions bump the nonce, they are considered to have happened and therefore produce a receipt. From c2dc390b62bb1f5a98c64c90706a6c9f6ed4c8dd Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:18:20 +0000 Subject: [PATCH 38/43] docs: Distribution reward queries (DelegationRewards / DelegationTotalRewards) are now read-only and no longer mutate validator-period state as of v6.7.0, making them idempotent. (sei-protocol/sei-chain#3738) --- evm/precompiles/distribution.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evm/precompiles/distribution.mdx b/evm/precompiles/distribution.mdx index 360ff67..0399c1f 100644 --- a/evm/precompiles/distribution.mdx +++ b/evm/precompiles/distribution.mdx @@ -262,6 +262,10 @@ require(success, "Failed to withdraw commission"); Returns the reward information for a delegator across all validators. + + +**Read-only as of v6.7.0 (#)**: The distribution reward query — including this `rewards()` precompile, the gRPC `DelegationRewards`/`DelegationTotalRewards` queries, and the legacy querier — is read-only and idempotent as of v6.7.0. It computes outstanding rewards without mutating validator-period state, so repeated calls return identical results and leave distribution state unchanged. Before v6.7.0 the query incremented the validator period on each call. The old mutating behavior is reproduced only when re-tracing pre-v6.7.0 blocks via `debug_trace*`. + ```solidity function rewards(address delegatorAddress) external view returns (Rewards rewards); ``` From 4311939248e85ce71953655fa63e8bcaa7b4c078 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:19:04 +0000 Subject: [PATCH 39/43] docs: The SEI_CONFIG_MANAGER=v2 environment variable now activates a sei-config-backed manager that performs advisory config validation (logging warnings for invalid fields) while booting identically to legacy; previously v2 was an unimplemented stub that errored. (sei-protocol/sei-chain#3678) --- node/technical-reference.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 6e43d17..cbc4c35 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -42,7 +42,7 @@ seid query node info `seid` reads the experimental `SEI_CONFIG_MANAGER` environment variable to select which configuration manager resolves a node's configuration at startup. The value is matched exactly — it is not trimmed or case-folded. - Unset or `legacy`: uses the existing legacy config loader. This is the default and leaves the configuration path unchanged. -- `v2`: selects the new sei-config-backed manager. This manager is not yet implemented and fails fast with a hard error (`SEI_CONFIG_MANAGER=v2 not yet implemented`) rather than silently falling back to the legacy path, so a `v2` invocation is observable. +- `v2`: selects the sei-config-backed manager. This manager boots the node identically to the legacy path — it re-enters the legacy reader on your original `config.toml` and `app.toml`, and never rewrites those files or refuses boot. In addition, it runs an advisory config-validation pass that logs diagnostics at warn level (for example, a missing `chain.min_gas_prices`) without changing the boot outcome. Validation is non-fatal: a diagnostic is informational only. Because the pass reads the on-disk config before the node generates its own files, a brand-new node is not validated on its first boot; diagnostics first appear from the second start onward. - Any other value: `seid` refuses to start and reports an `invalid SEI_CONFIG_MANAGER` error naming the legal tokens (unset, `legacy`, or `v2`). There is no silent fallback. ```bash @@ -52,7 +52,8 @@ seid start # Equivalent to the default SEI_CONFIG_MANAGER=legacy seid start -# Selects the v2 manager, which currently returns a hard error +# Selects the v2 manager, which boots identically to legacy while emitting +# advisory config-validation warnings to the logs SEI_CONFIG_MANAGER=v2 seid start ``` From d30f9c63254b9f70ecd1a5ce272aebfb2858d437 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:19:34 +0000 Subject: [PATCH 40/43] docs: The slashing precompile adds a new unjail() transaction method allowing a validator operator to unjail their validator via the EVM precompile at the slashing precompile address. (sei-protocol/sei-chain#3825) --- evm/precompiles/example-usage.mdx | 2 +- evm/precompiles/slashing.mdx | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/evm/precompiles/example-usage.mdx b/evm/precompiles/example-usage.mdx index 70d6cdd..5d336f7 100644 --- a/evm/precompiles/example-usage.mdx +++ b/evm/precompiles/example-usage.mdx @@ -29,7 +29,7 @@ Sei precompiles are special smart contracts deployed at fixed addresses. They ex | P256 | `0x0000000000000000000000000000000000001011` | Verify P-256 elliptic curve signatures | | Mint | `0x0000000000000000000000000000000000001012` | Query the mint params and minter state | | Params | `0x0000000000000000000000000000000000001013` | Query module params by subspace and key | -| Slashing | `0x0000000000000000000000000000000000001014` | Query slashing params and validator signing info | +| Slashing | `0x0000000000000000000000000000000000001014` | Query slashing params and validator signing info, and unjail a validator via `unjail()` | | Upgrade | `0x0000000000000000000000000000000000001015` | Query the current/applied upgrade plan and module versions | This page shows Bank, Staking, Governance, Distribution, JSON, and Solo. The other precompiles have their own pages: [CosmWasm](/evm/precompiles/cosmwasm-precompiles/cosmwasm), [Address](/evm/precompiles/cosmwasm-precompiles/addr), [Pointer contracts](/evm/evm-parity/examples/pointer-contracts) for both pointer precompiles, and [P256](/evm/precompiles/p256-precompile). diff --git a/evm/precompiles/slashing.mdx b/evm/precompiles/slashing.mdx index 2ba8739..f4f0673 100644 --- a/evm/precompiles/slashing.mdx +++ b/evm/precompiles/slashing.mdx @@ -109,6 +109,26 @@ Returns the signing information for a single validator identified by its consens Querying a consensus address with no signing information reverts. + + +### `unjail` + +```solidity +function unjail() external returns (bool success); +``` + +Unjails the validator whose operator address is the caller's associated Sei address. This is a state-mutating transaction, so the caller's EVM address must be associated with a Sei address, and that Sei address must be a validator operator. + +**Returns** + +| Field | Type | Description | +| --------- | ------ | ---------------------------------------- | +| `success` | `bool` | `true` when the validator was unjailed. | + +The call reverts if the caller has no associated Sei address, if that address is not a validator operator, or if the validator does not currently satisfy the conditions required to be unjailed. + +Because this method acts on behalf of the caller, it is non-payable and cannot be invoked via `delegatecall` or `staticcall`; such calls revert. + ### `signingInfos` ```solidity From 0b15a48a1d1e5e73047c2cab3f64317c05b75a87 Mon Sep 17 00:00:00 2001 From: "seidroid[bot]" <257742136+seidroid[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:21:40 +0000 Subject: [PATCH 41/43] docs: EVM WebSocket plane (:8546) now shares HTTP admission-control config (max_request_body_bytes, max_concurrent_request_bytes) and adds a new ws_admission_timeout config field, with the WS frame size default dropping from 10 MiB to 5 MiB requiring operator action. (sei-protocol/sei-chain#3818) --- evm/evm-parity/websocket.mdx | 20 ++++++++++++++++++++ node/advanced-config-monitoring.mdx | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/evm/evm-parity/websocket.mdx b/evm/evm-parity/websocket.mdx index f8f84ea..3a680cb 100644 --- a/evm/evm-parity/websocket.mdx +++ b/evm/evm-parity/websocket.mdx @@ -115,6 +115,26 @@ const unwatch = client.watchEvent({ - Pending transaction subscriptions (`newPendingTransactions`) are supported at the RPC level, but Sei does not guarantee Ethereum-style pending state visibility. +## Frame size and concurrency limits + +The WebSocket plane shares its admission-control settings with the HTTP JSON-RPC plane through the `[evm]` section of `app.toml`. Two limits apply to every WebSocket connection: + +### Frame size (`max_request_body_bytes`) + +Each inbound WebSocket frame is capped by `[evm].max_request_body_bytes`, the same knob that bounds HTTP request bodies. The default is 5 MiB (`5242880`). Frames larger than this limit cause the connection to close with WebSocket close code `1009` (message too big); no JSON-RPC error response is returned. + + + Earlier node releases used a hardcoded 10 MiB frame cap on the WebSocket plane. Both planes now share `max_request_body_bytes` with a 5 MiB default. Operators whose WebSocket clients send frames in the 5–10 MiB range (for example large `eth_sendRawTransaction` batches or wide filter payloads) should set `max_request_body_bytes = 10485760` in `app.toml` before upgrading. Note that this also raises the HTTP body limit to 10 MiB. + + +### Concurrent request budget (`max_concurrent_request_bytes` and `ws_admission_timeout`) + +`[evm].max_concurrent_request_bytes` bounds the total size of JSON-RPC request bytes admitted for processing concurrently. The WebSocket plane and the HTTP plane each get their own independent budget, so peak in-flight request bytes process-wide can reach twice this value. + +When a WebSocket connection cannot be admitted immediately because the budget is exhausted, it blocks and waits for budget to free up. `[evm].ws_admission_timeout` bounds how long it waits (default `30s`; zero or negative values use the go-ethereum default of 30s). If the wait expires, the peer receives JSON-RPC error `-32005` ("timed out waiting for concurrent request-byte budget") and the connection is closed, dropping any active subscriptions along with it. + + + ## `newHeads` under Autobahn consensus diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index e57a897..431c22b 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -583,7 +583,7 @@ Every OpenTelemetry metric series exported through the Prometheus exporter (the | `evmrpc_websocket_connects_total` | Counter | Number of new WebSocket connections. | | `evmrpc_redirected_requests_total` | Counter | Number of EVM RPC requests forwarded to another validator. Labeled by `endpoint` and `connection`. | | `evmrpc_historical_debug_trace_attempts_total` | Counter | Number of `debug_trace*` requests that target historical blocks beyond the configured max block lookback. Labeled by `endpoint` and `connection`. | -| `evmrpc_requests_rejected_total` | Counter | Number of HTTP JSON-RPC requests rejected by pre-decode admission control. Labeled by `reason`, which is `oversize` when a request body exceeds the configured `max_request_body_bytes` cap (rejected with HTTP 413) or `busy` when the `max_concurrent_request_bytes` budget is exhausted (rejected with HTTP 429). | +| `evmrpc_requests_rejected_total` | Counter | Number of JSON-RPC requests rejected by admission control. Labeled by `protocol` (`http` or `ws`) and `reason` (`oversize` or `busy`). On HTTP (:8545), an `oversize` request body exceeds the configured `max_request_body_bytes` cap and is rejected with HTTP 413, while `busy` means the `max_concurrent_request_bytes` budget is exhausted and the request is rejected with HTTP 429. On WebSocket (:8546), `oversize` frames exceed `max_request_body_bytes` and close the connection with WebSocket close code 1009 (no JSON-RPC error response), while `busy` means the connection waited for concurrent-byte budget longer than `ws_admission_timeout` and the peer receives JSON-RPC error `-32005` before the connection is closed (#3818). | The `evmrpc_request_latency_seconds` histogram carries these labels: From c62b6b12de7dfc91a8003cd75d5e0a135ea840ab Mon Sep 17 00:00:00 2001 From: alexander-sei Date: Thu, 15 Oct 2026 14:01:25 +0200 Subject: [PATCH 42/43] Refresh EVM docs for v6.7 This commit updates the documentation set to reflect the v6.7 Sei EVM behavior and operational changes. It removes the deprecated `eth_getProof` and feegrant pages, adds a redirect to the new proof-verification section, updates EVM compatibility and RPC docs, and rewrites the precompile pages for the newer query APIs and gas behavior. It also refreshes tracing, monitoring, node migration, and Autobahn guidance to match the current config, retention, and HashVault behavior. --- .github/styles/Sei/Headings.yml | 2 + docs.json | 7 +- evm/differences-with-ethereum.mdx | 6 +- evm/evm-parity/evm-compatibility.mdx | 6 +- evm/evm-parity/gas-and-fees.mdx | 45 +--- evm/evm-parity/state-proofs.mdx | 84 ------ evm/evm-parity/websocket.mdx | 20 +- evm/precompiles/auth.mdx | 88 ++----- evm/precompiles/authz.mdx | 107 ++------ evm/precompiles/cosmwasm-precompiles/addr.mdx | 2 - evm/precompiles/cosmwasm-precompiles/bank.mdx | 160 +++++------- evm/precompiles/distribution.mdx | 6 +- evm/precompiles/evidence.mdx | 71 +---- evm/precompiles/example-usage.mdx | 19 +- evm/precompiles/feegrant.mdx | 96 ------- evm/precompiles/governance.mdx | 7 +- evm/precompiles/json.mdx | 2 +- evm/precompiles/mint.mdx | 79 +----- evm/precompiles/p256-precompile.mdx | 11 +- evm/precompiles/params.mdx | 57 +---- evm/precompiles/slashing.mdx | 160 +++--------- evm/precompiles/upgrade.mdx | 119 ++------- evm/reference.mdx | 114 ++------- evm/tracing/index.mdx | 44 +--- evm/tracing/javascript-tracers.mdx | 8 +- evm/tracing/troubleshooting.mdx | 38 +-- evm/transactions.mdx | 19 -- learn/sei-giga-developers.mdx | 4 +- node/advanced-config-monitoring.mdx | 145 ++++------- node/giga-storage-migration.mdx | 231 +++++------------ node/node-operators.mdx | 100 ++------ node/node-types.mdx | 2 +- node/technical-reference.mdx | 242 ++++++------------ node/troubleshooting.mdx | 67 ++--- snippets/rpc-methods-viewer.jsx | 5 +- 35 files changed, 514 insertions(+), 1659 deletions(-) delete mode 100644 evm/evm-parity/state-proofs.mdx delete mode 100644 evm/precompiles/feegrant.mdx diff --git a/.github/styles/Sei/Headings.yml b/.github/styles/Sei/Headings.yml index 1537c1f..75eb051 100644 --- a/.github/styles/Sei/Headings.yml +++ b/.github/styles/Sei/Headings.yml @@ -41,6 +41,8 @@ exceptions: - Ledger - Twin Turbo Consensus - Autobahn + - HashVault + - Prometheus - Pectra - RPC - API diff --git a/docs.json b/docs.json index 3ba1364..8946d57 100644 --- a/docs.json +++ b/docs.json @@ -128,7 +128,6 @@ "evm/evm-parity/finality", "evm/evm-parity/gas-and-fees", "evm/evm-parity/transaction-types", - "evm/evm-parity/state-proofs", "evm/evm-parity/websocket", "evm/evm-parity/signing" ] @@ -202,7 +201,6 @@ "evm/precompiles/auth", "evm/precompiles/authz", "evm/precompiles/evidence", - "evm/precompiles/feegrant", "evm/precompiles/mint", "evm/precompiles/params", "evm/precompiles/slashing", @@ -1745,6 +1743,11 @@ "source": "/learn/sip-03-exchange-migration", "destination": "/learn/sip-03-migration", "permanent": true + }, + { + "source": "/evm/evm-parity/state-proofs", + "destination": "/evm/evm-parity/evm-compatibility#proof-verification", + "permanent": true } ], "interaction": { diff --git a/evm/differences-with-ethereum.mdx b/evm/differences-with-ethereum.mdx index aa376a1..f3ca9c9 100644 --- a/evm/differences-with-ethereum.mdx +++ b/evm/differences-with-ethereum.mdx @@ -69,7 +69,7 @@ Sei EVM was first deployed at these block heights and versions: | PREVRANDAO | Returns a value derived from the current block time | Returns the RANDAO mix (EIP‑4399) | Not a randomness source. Use an oracle/VRF. `DIFFICULTY` aliases to this. | | COINBASE | Always the global fee collector address | Block proposer (miner) address | Do not assume it is the validator address. | | BASEFEE | Returns the current base fee, with no burn | Returns the current base fee. A portion is burned (EIP‑1559). | Legacy transactions must meet Sei's governance-set minimum gas price (currently `50 gwei`). Query `eth_gasPrice` for the live value. | -| BLOCKHASH | Hash of the Tendermint header, with a different encoding | Keccak of the Ethereum block header | Usable for recent blocks only. Values are not interchangeable across chains. | +| BLOCKHASH | Hash of the Tendermint header, with a different encoding | Keccak of the Ethereum block header | Returns hashes for the previous 256 blocks, as on Ethereum. Older, current, and future blocks return zero. Values are not interchangeable across chains. | | GASLIMIT | = 12,500,000 per block | = 60,000,000 per block | Represents the block gas limit on both chains. | | TIMESTAMP | Tendermint block time | Proposer-chosen block time | Same semantics. Do not use it for randomness. | | DIFFICULTY | Alias of PREVRANDAO | Alias of PREVRANDAO (EIP‑4399) | Returns the same value as PREVRANDAO. | @@ -101,10 +101,6 @@ state root, which is an MPT root. Sei computes the block hash from the block header in Tendermint data format. As a result, it is different from Ethereum's block hash. -The `BLOCKHASH` opcode returns the correct historical block hash for the previous 256 blocks, matching the EVM Yellow Paper's availability window. Sei maintains an on-chain ring of recent block hashes that is populated during `BeginBlock` from the parent block's hash, backed by a process-local cache, with a fallback to staking historical info. Before this change, `BLOCKHASH` for recent blocks (such as `height - 1`) returned an empty hash. Hashes outside the 256-block window, or hashes of future/current-height blocks, return zero, and `BLOCKHASH` values remain non-interchangeable with Ethereum because of the different header encoding. - -{/* For additional context, see the opcode differences table above. */} - ## Base fee & tips Sei supports all non‑blob transaction types, including the Pectra `SetCode` transaction (EIP‑7702). However, for a legacy (non EIP‑1559) diff --git a/evm/evm-parity/evm-compatibility.mdx b/evm/evm-parity/evm-compatibility.mdx index a5c8f47..cc9947f 100644 --- a/evm/evm-parity/evm-compatibility.mdx +++ b/evm/evm-parity/evm-compatibility.mdx @@ -37,7 +37,7 @@ Standard EVM tools (viem, wagmi, ethers, Foundry, and Hardhat) work on Sei for a | `eth_getTransactionCount` (nonce) | Supported | | | `eth_getCode` | Supported | | | `eth_getStorageAt` | Supported with differences | Governance can adjust the SSTORE cost. Do not hard-code gas assumptions. [See Gas and Fees.](/evm/evm-parity/gas-and-fees) | -| `eth_getProof` | Supported with differences | Returns IAVL proof data, not Ethereum Merkle Patricia Trie proofs. Proof verification logic must account for this. Proofs resolve across the supported store backends (classic IAVL, store/v2 memiavl, and other proof-capable queryable stores). As a result, the method works across a broader range of node configurations. [See State Proofs.](/evm/evm-parity/state-proofs) | +| `eth_getProof` | Not supported | Since v6.7.0, the method returns JSON-RPC error `-32000`. [See Proof verification.](#proof-verification) | ## Blocks and finality @@ -52,7 +52,7 @@ Standard EVM tools (viem, wagmi, ethers, Foundry, and Hardhat) work on Sei for a | Feature | Sei status | Notes | | --- | --- | --- | -| `eth_getLogs` | Supported with differences | A single query may match at most `max_log_no_block` logs (default 10000) and `max_log_bytes` of estimated heap bytes (default 64 MiB). This applies to both bounded and open-ended block ranges. Exceeding either limit returns an error (`too many logs` / `too many log bytes`) advising you to narrow the block range or filter criteria, rather than silently truncating the result. [See RPC configuration.](/node/node-operators/rpc-configuration) | +| `eth_getLogs` | Supported with differences | A query fails when it matches more than 10,000 logs (`max_log_no_block`) or about 64 MiB of log data (`max_log_bytes`). The query does not return partial results. Narrow the block range or the filters, then try again. [See Filter and subscription limits.](/evm/reference#filter-and-subscription-limits) | | Filter lifecycle (`eth_newFilter`, `eth_getFilterChanges`, `eth_getFilterLogs`, `eth_uninstallFilter`) | Supported | | | WebSocket subscriptions (`eth_subscribe`) | Supported | [See WebSocket Connections.](/evm/evm-parity/websocket) | @@ -128,7 +128,7 @@ These JSON-RPC methods are not available on Sei or return errors: | Method | Behavior on Sei | | --- | --- | -| `eth_getProof` | Returns IAVL tree proofs, not Ethereum Merkle Patricia Trie proofs. The method works, but the proof format is not compatible with standard Ethereum MPT verifiers. [See State Proofs.](/evm/evm-parity/state-proofs) | +| `eth_getProof` | Not supported since v6.7.0. Sei registers the method, but every call returns JSON-RPC error `-32000` with the message `eth_getProof is not supported yet; please reach out to the Sei Labs if you need this endpoint`. | ### Deprecated / removed opcodes diff --git a/evm/evm-parity/gas-and-fees.mdx b/evm/evm-parity/gas-and-fees.mdx index 1ec9109..5367c6f 100644 --- a/evm/evm-parity/gas-and-fees.mdx +++ b/evm/evm-parity/gas-and-fees.mdx @@ -92,47 +92,16 @@ const gas = await provider.estimateGas({ Estimate the absolute storage-write cost with a live `eth_estimateGas` call against a Sei RPC. A Foundry `forge test --gas-report --fork-url ` report forks the chain *state*, but it runs the standard EVM gas schedule of revm. It therefore shows `SSTORE` at the Ethereum cost (approximately 22,100) instead of the Sei cost (approximately 72,000). Use the report for relative profiling of your own logic. +## Gas for precompile calls -## Precompile calldata decode cost +Sei precompiles charge gas based on the work that each call does: -Sei's dynamic-gas precompiles (such as bank, wasmd, oracle, ibc, json, distribution, pointer, pointerview, addr, and p256) now charge gas for ABI-decoding a call's calldata *before* the arguments are decoded. The charge covers a length-proportional scan of the input plus the cost of copying any `string` payloads the decoder would materialize — a cost that can be significantly larger than the raw calldata length when a single string is referenced by many array or tuple slots. - -The `json`, `p256`, and `pointerview` precompiles use this dynamic gas model as well. Their metering works as follows: - -- **`json`** — charges 100 gas per byte of the JSON payload being parsed, consumed against the call's gas meter. -- **`pointerview`** — self-meters based on the state reads performed during pointer lookups, rather than a flat charge. -- **`p256`** — charges a fixed **3,450 gas** verification cost, matching RIP-7212's `P256VERIFY`. Because this flat charge is levied outside the verification's panic recovery, a call that forwards too little gas to cover it fails the transaction with an out-of-gas error rather than reverting the call. - -Two consequences for callers: - -- A precompile call that forwards too little gas to cover the decode is reverted before the precompile runs, instead of being decoded for free. -- Overall gas used for dynamic-gas precompile calls has increased slightly, since the decode is now metered. - -Because this charge depends on the exact shape and size of your calldata, do not hard-code gas limits for precompile calls. Always size them with a live `eth_estimateGas` call against a Sei RPC: - - - -```ts viem -const gas = await client.estimateGas({ - account, - to: precompileAddress, - data: encodedCalldata, -}); - -// Add a buffer for safety during high-activity periods -const gasWithBuffer = (gas * 120n) / 100n; // 20% buffer -``` - -```ts ethers -const gas = await provider.estimateGas({ - from: signer.address, - to: precompileAddress, - data: encodedCalldata, -}); -``` - - +- Before a precompile decodes its arguments, it charges gas for the calldata. The charge grows with the calldata length and with the size of any `string` arguments. A call that cannot pay this charge reverts before the precompile runs. +- The JSON precompile charges 100 gas per byte of JSON input. +- The pointer view precompile charges for the state that each lookup reads. +- The P256 precompile charges a fixed 3,450 gas per verification, the same as RIP-7212 `P256VERIFY`. If the call cannot pay it, the call fails with an out-of-gas error. +Because the cost depends on your calldata, do not hard-code gas limits for precompile calls. Use `eth_estimateGas`, as shown in the example above. ## Summary diff --git a/evm/evm-parity/state-proofs.mdx b/evm/evm-parity/state-proofs.mdx deleted file mode 100644 index 5f9cf07..0000000 --- a/evm/evm-parity/state-proofs.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: 'State Proofs' -description: 'How eth_getProof differs on Sei due to IAVL tree storage' ---- - -# State proofs - -Sei supports `eth_getProof`, but it returns a different proof format from Ethereum. If your application verifies proofs on-chain or off-chain, account for this difference. - -## The difference - -Ethereum stores state in a Merkle Patricia Trie (MPT), and `eth_getProof` returns MPT inclusion proofs. Sei stores state in an IAVL tree and returns IAVL proofs instead. - -The RPC method exists and responds correctly, but the proof data structure is not compatible with Ethereum MPT proof verifiers. - -### Store backends - -`eth_getProof` resolves the underlying account store. It unwraps known KVStore wrappers until it reaches a proof-capable queryable store. As a result, proofs work across a broader range of node configurations, not only with a classic IAVL store. Supported roots include: - -- Classic IAVL stores -- store/v2 memiavl commitment stores -- Any other proof-capable (queryable) store reached through cache, `tracekv`, Giga cache, or prefix store wrappers - -If the call cannot reach any of these stores, it returns a `cannot find a proof-capable queryable KV store` error. In all cases, the returned proof data is in IAVL format, not Ethereum MPT format. - -## What this affects - -Most applications do not call `eth_getProof` directly. Its main users are: - -- Light clients that verify state without trusting an RPC node -- Cross-chain bridges that prove the inclusion of state on Sei -- Applications that verify contract storage values trustlessly - -If you do standard contract reads, event queries, or transaction lookups, this difference does not affect you. - -## Calling eth_getProof - -### Storage key requirements - -Before you call `eth_getProof`, note these two requirements that Sei enforces on the `storageKeys` argument: - -- **Keys must be hex-encoded.** Each storage key must be a valid hex-encoded value (for example, `0x0000000000000000000000000000000000000000000000000000000000000001`). Sei decodes each key and left-pads it to 32 bytes. Sei rejects a malformed, non-hex key with an `invalid storage key` error. Raw byte strings do not work, although earlier versions accepted them. -- **At most 1024 keys per request.** A single proof request may include a maximum of 1024 storage keys. A request for more keys returns a `too many storage keys` error. Split larger sets across multiple requests. - -The call works with standard libraries: - - - -```ts viem -import { createPublicClient, http } from 'viem'; -import { sei } from 'viem/chains'; - -const client = createPublicClient({ chain: sei, transport: http() }); - -const proof = await client.getProof({ - address: '0xContractAddress', - storageKeys: ['0xStorageSlot'], -}); - -// proof.accountProof and proof.storageProof contain IAVL proof data, -// not Ethereum MPT proofs — do not pass to an MPT verifier -``` - -```ts ethers -import { ethers } from 'ethers'; - -const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); - -const proof = await provider.send('eth_getProof', [ - '0xContractAddress', - ['0xStorageSlot'], - 'latest', -]); - -// Same caveat — proof data is IAVL format -``` - - - -## Verifying proofs - -To verify Sei state proofs, use an IAVL-compatible verifier. Standard Ethereum MPT verifier libraries reject Sei proofs. Examples are the libraries used in Solidity or in Ethereum bridge contracts. - -For the exact proof schema that the method returns, see the [Sei RPC reference](/evm/reference). diff --git a/evm/evm-parity/websocket.mdx b/evm/evm-parity/websocket.mdx index b3db123..e33177b 100644 --- a/evm/evm-parity/websocket.mdx +++ b/evm/evm-parity/websocket.mdx @@ -111,28 +111,18 @@ const unwatch = client.watchEvent({ - Sei has instant finality, so every block emitted over WebSocket is already final. You do not need to wait for more confirmations before you act on an event. - Pending transaction subscriptions (`newPendingTransactions`) are supported at the RPC level, but Sei does not guarantee Ethereum-style pending state visibility. - ## Frame size and concurrency limits -The WebSocket plane shares its admission-control settings with the HTTP JSON-RPC plane through the `[evm]` section of `app.toml`. Two limits apply to every WebSocket connection: - -### Frame size (`max_request_body_bytes`) +WebSocket connections use the same request limits as HTTP JSON-RPC. Operators set them in the `[evm]` section of `app.toml`: -Each inbound WebSocket frame is capped by `[evm].max_request_body_bytes`, the same knob that bounds HTTP request bodies. The default is 5 MiB (`5242880`). Frames larger than this limit cause the connection to close with WebSocket close code `1009` (message too big); no JSON-RPC error response is returned. +- `max_request_body_bytes` (default 5 MiB) caps each WebSocket frame and each HTTP request body. If a frame is larger, the node closes the connection with WebSocket close code `1009` and does not send a JSON-RPC error. +- `max_concurrent_request_bytes` (default 128 MiB) caps the request bytes that the node processes at the same time. WebSocket and HTTP each have their own budget. +- `ws_admission_timeout` (default `30s`) sets how long a WebSocket request waits for budget. When the wait expires, the client gets JSON-RPC error `-32005` and the node closes the connection, which ends its subscriptions. - Earlier node releases used a hardcoded 10 MiB frame cap on the WebSocket plane. Both planes now share `max_request_body_bytes` with a 5 MiB default. Operators whose WebSocket clients send frames in the 5–10 MiB range (for example large `eth_sendRawTransaction` batches or wide filter payloads) should set `max_request_body_bytes = 10485760` in `app.toml` before upgrading. Note that this also raises the HTTP body limit to 10 MiB. +Before v6.7.0, the WebSocket frame limit was 10 MiB. If your clients send frames between 5 MiB and 10 MiB, set `max_request_body_bytes = 10485760` in `app.toml` before you upgrade. This setting also raises the HTTP body limit to 10 MiB. -### Concurrent request budget (`max_concurrent_request_bytes` and `ws_admission_timeout`) - -`[evm].max_concurrent_request_bytes` bounds the total size of JSON-RPC request bytes admitted for processing concurrently. The WebSocket plane and the HTTP plane each get their own independent budget, so peak in-flight request bytes process-wide can reach twice this value. - -When a WebSocket connection cannot be admitted immediately because the budget is exhausted, it blocks and waits for budget to free up. `[evm].ws_admission_timeout` bounds how long it waits (default `30s`; zero or negative values use the go-ethereum default of 30s). If the wait expires, the peer receives JSON-RPC error `-32005` ("timed out waiting for concurrent request-byte budget") and the connection is closed, dropping any active subscriptions along with it. - - - - ## `newHeads` under Autobahn consensus When a node runs under Autobahn consensus, `eth_subscribe("newHeads")` notifications come from an in-process notifier, not from the legacy consensus event bus. The notifier publishes committed-block headers directly. Subscribers still observe headers only for fully committed blocks. However, the header payload differs from the legacy path in a few ways: diff --git a/evm/precompiles/auth.mdx b/evm/precompiles/auth.mdx index 86c7b6b..4a90056 100644 --- a/evm/precompiles/auth.mdx +++ b/evm/precompiles/auth.mdx @@ -1,17 +1,16 @@ --- -title: Auth Precompile -description: Query account information, auth module parameters, and the next account number from the EVM. +title: 'Auth Precompile' +sidebarTitle: 'Auth' +description: 'Read account details, auth module parameters, and the next account number from EVM contracts with the Auth precompile.' +keywords: ['auth precompile', 'account query', 'account number', 'account sequence', 'sei evm'] --- +**Address:** `0x000000000000000000000000000000000000100D` -The Auth precompile exposes read-only access to the Cosmos SDK `auth` module, letting Solidity contracts look up account details, the auth module parameters, and the next account number. All of its methods are views and never mutate state. +The Auth precompile reads data from the Cosmos `auth` module: account details, module parameters, and the next account number. All methods are `view` functions. The precompile is available since v6.7.0. -## Address +## Interface -The Auth precompile is available at the fixed address `0x000000000000000000000000000000000000100D`. - -## Solidity Interface - -```solidity +```solidity Auth.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -20,40 +19,11 @@ address constant AUTH_PRECOMPILE_ADDRESS = 0x00000000000000000000000000000000000 IAuth constant AUTH_CONTRACT = IAuth(AUTH_PRECOMPILE_ADDRESS); interface IAuth { - // Queries - - /** - * @notice Get account information for the given address - * @param addr The EVM address (must be associated with a Sei address) - * @return account Account details - */ - function account( - address addr - ) external view returns (Account memory account); - - /** - * @notice Get all accounts, paginated - * @param pageKey Pagination key (empty bytes for the first page) - * @return response Accounts response with pagination - */ - function accounts( - bytes memory pageKey - ) external view returns (AccountsResponse memory response); - - /** - * @notice Get the auth module parameters - * @return params Auth module parameters - */ + function account(address addr) external view returns (Account memory account); + function accounts(bytes memory pageKey) external view returns (AccountsResponse memory response); function params() external view returns (AuthParams memory params); - - /** - * @notice Get the next account number - * @return count The next account number - */ function nextAccountNumber() external view returns (uint64 count); - // Structs - struct Account { string accountAddress; uint64 accountNumber; @@ -78,34 +48,14 @@ interface IAuth { ## Methods -### account - -```solidity -function account(address addr) external view returns (Account memory account); -``` - -Returns the `Account` details (address, account number, and sequence) for the Sei address associated with `addr`. The EVM address must be associated with a Sei address; if it is not, or if no account exists for the resolved address, the call reverts. +| Method | Returns | +| --- | --- | +| `account(addr)` | The Sei address, account number, and sequence of the account linked to `addr`. | +| `accounts(pageKey)` | All accounts, one page at a time. | +| `params()` | The `auth` module parameters. | +| `nextAccountNumber()` | The account number that the next new account gets. The call does not change the counter. | -### accounts - -```solidity -function accounts(bytes memory pageKey) external view returns (AccountsResponse memory response); -``` - -Returns a paginated list of all accounts. Pass empty `bytes` for the first page, then supply the returned `nextKey` to fetch subsequent pages. - -### params - -```solidity -function params() external view returns (AuthParams memory params); -``` - -Returns the auth module parameters, including `maxMemoCharacters`, `txSigLimit`, `txSizeCostPerByte`, `sigVerifyCostEd25519`, `sigVerifyCostSecp256k1`, and `disableSeqnoCheck`. - -### nextAccountNumber - -```solidity -function nextAccountNumber() external view returns (uint64 count); -``` +## Notes -Returns the next account number. This is a read-only view and does not increment the persisted global account number counter, so repeated calls return the same value. +- `account()` reverts if `addr` has no associated Sei address, or if no account exists for it. +- For `accounts()`, pass empty bytes for the first page, then pass the returned `nextKey`. An empty `nextKey` means there are no more pages. diff --git a/evm/precompiles/authz.mdx b/evm/precompiles/authz.mdx index 8c659cf..52e5ba3 100644 --- a/evm/precompiles/authz.mdx +++ b/evm/precompiles/authz.mdx @@ -1,21 +1,16 @@ --- -title: Authz Precompile -description: Query authorization grants from the Cosmos authz module directly from EVM smart contracts on Sei. +title: 'Authz Precompile' +sidebarTitle: 'Authz' +description: 'Read Cosmos authz grants between accounts from EVM contracts with the Authz precompile.' +keywords: ['authz precompile', 'authorization grants', 'granter', 'grantee', 'sei evm'] --- +**Address:** `0x000000000000000000000000000000000000100E` -The Authz precompile exposes read-only queries from the Cosmos `authz` module to EVM smart contracts. It lets contracts inspect authorization grants between a granter and a grantee, as well as all grants issued or received by a given account. - -## Address - -The Authz precompile is available at the fixed address: - -``` -0x000000000000000000000000000000000000100E -``` +The Authz precompile reads grants from the Cosmos `authz` module. A grant lets a grantee account send specific messages for a granter account. All methods are `view` functions. The precompile is available since v6.7.0. ## Interface -```solidity +```solidity Authz.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -24,16 +19,6 @@ address constant AUTHZ_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000 IAuthz constant AUTHZ_CONTRACT = IAuthz(AUTHZ_PRECOMPILE_ADDRESS); interface IAuthz { - // Queries - - /** - * @notice Get grants between a granter and a grantee, optionally filtered by message type URL - * @param granter The granter's EVM address (must be associated with a Sei address) - * @param grantee The grantee's EVM address (must be associated with a Sei address) - * @param msgTypeUrl The message type URL to filter by (empty string for all) - * @param pageKey Pagination key (empty bytes for the first page) - * @return response Grants response with pagination - */ function grants( address granter, address grantee, @@ -41,30 +26,16 @@ interface IAuthz { bytes memory pageKey ) external view returns (GrantsResponse memory response); - /** - * @notice Get all grants granted by a granter - * @param granter The granter's EVM address (must be associated with a Sei address) - * @param pageKey Pagination key (empty bytes for the first page) - * @return response Grant authorizations response with pagination - */ function granterGrants( address granter, bytes memory pageKey ) external view returns (GrantAuthorizationsResponse memory response); - /** - * @notice Get all grants granted to a grantee - * @param grantee The grantee's EVM address (must be associated with a Sei address) - * @param pageKey Pagination key (empty bytes for the first page) - * @return response Grant authorizations response with pagination - */ function granteeGrants( address grantee, bytes memory pageKey ) external view returns (GrantAuthorizationsResponse memory response); - // Structs - struct Grant { bytes authorization; int64 expiration; @@ -91,61 +62,15 @@ interface IAuthz { ## Methods -All methods on this precompile are read-only `view` queries. They do not mutate state. - -### grants - -Returns the grants between a specific granter and grantee, optionally filtered by a message type URL. - -```solidity -function grants( - address granter, - address grantee, - string memory msgTypeUrl, - bytes memory pageKey -) external view returns (GrantsResponse memory response); -``` - -- `granter` — the granter's EVM address, which must be associated with a Sei address. -- `grantee` — the grantee's EVM address, which must be associated with a Sei address. -- `msgTypeUrl` — the message type URL to filter by. Pass an empty string to return all grants. -- `pageKey` — the pagination key; pass empty bytes to request the first page. - -Each returned `Grant` contains the `authorization` encoded as JSON bytes and an `expiration` expressed as a Unix timestamp in seconds. The `nextKey` field of the response is the pagination key for the next page (empty when the result set is exhausted). - -### granterGrants - -Returns every grant issued by a given granter. - -```solidity -function granterGrants( - address granter, - bytes memory pageKey -) external view returns (GrantAuthorizationsResponse memory response); -``` - -- `granter` — the granter's EVM address, which must be associated with a Sei address. -- `pageKey` — the pagination key; pass empty bytes to request the first page. - -Each returned `GrantAuthorization` includes the bech32 `granter` and `grantee` addresses, the `authorization` encoded as JSON bytes, and an `expiration` as a Unix timestamp in seconds. - -### granteeGrants - -Returns every grant received by a given grantee. - -```solidity -function granteeGrants( - address grantee, - bytes memory pageKey -) external view returns (GrantAuthorizationsResponse memory response); -``` - -- `grantee` — the grantee's EVM address, which must be associated with a Sei address. -- `pageKey` — the pagination key; pass empty bytes to request the first page. - -The response mirrors `granterGrants`, listing each `GrantAuthorization` with its granter, grantee, JSON-encoded authorization, and expiration. +| Method | Returns | +| --- | --- | +| `grants(granter, grantee, msgTypeUrl, pageKey)` | The grants from `granter` to `grantee`. Pass an empty `msgTypeUrl` to get grants for every message type. | +| `granterGrants(granter, pageKey)` | Every grant that `granter` issued. | +| `granteeGrants(grantee, pageKey)` | Every grant that `grantee` received. | ## Notes -- The addresses passed to these methods must be associated with a Sei address; querying an unassociated address reverts. -- The `authorization` field is returned as JSON-encoded bytes and includes an `@type` discriminator (for example, a `SendAuthorization`). +- Every address argument must have an associated Sei address. Otherwise, the call reverts. +- `authorization` is the grant's authorization encoded as JSON. It includes an `@type` field that names the authorization type. +- `expiration` is a Unix timestamp in seconds. In `GrantAuthorization`, `granter` and `grantee` are Sei addresses. +- For paginated methods, pass empty bytes for the first page, then pass the returned `nextKey`. An empty `nextKey` means there are no more pages. diff --git a/evm/precompiles/cosmwasm-precompiles/addr.mdx b/evm/precompiles/cosmwasm-precompiles/addr.mdx index a637487..dec6338 100644 --- a/evm/precompiles/cosmwasm-precompiles/addr.mdx +++ b/evm/precompiles/cosmwasm-precompiles/addr.mdx @@ -65,8 +65,6 @@ function associatePubKey( ) external returns (string memory seiAddr, address evmAddr); ``` - Both `associate()` and `associatePubKey()` are classified as transaction (state-mutating) methods on the precompile — the precompile's `IsTransaction` returns true for each of them — so each must be sent as a transaction rather than called as a read-only view. - ### Query functions ```solidity diff --git a/evm/precompiles/cosmwasm-precompiles/bank.mdx b/evm/precompiles/cosmwasm-precompiles/bank.mdx index 7d5476d..3f4e840 100644 --- a/evm/precompiles/cosmwasm-precompiles/bank.mdx +++ b/evm/precompiles/cosmwasm-precompiles/bank.mdx @@ -30,96 +30,6 @@ function sendNative( The examples below use `balance()` with `usei`. They do not cover arbitrary Bank Module denominations. - -## Read-only query methods - -The Bank precompile also exposes a set of read-only query methods that mirror the Bank Module's gRPC queries. - -```solidity -/// Returns the spendable (unlocked) balances of an account, paginated. -function spendableBalances( - address acc, - bytes memory pageKey -) external view returns (Coin[] memory balances, bytes memory nextKey); - -/// Returns the total supply of a single denomination. -function supply( - string memory denom -) external view returns (uint256 response); - -/// Returns the total supply of every denomination, paginated. -function totalSupply( - bytes memory pageKey -) external view returns (Coin[] memory supply, bytes memory nextKey); - -/// Returns the Bank Module parameters. -function params() external view returns (Params memory params); - -/// Returns the metadata for a single denomination. -function denomMetadata( - string memory denom -) external view returns (Metadata memory metadata); - -/// Returns the metadata for every denomination, paginated. -function denomsMetadata( - bytes memory pageKey -) external view returns (Metadata[] memory metadatas, bytes memory nextKey); -``` - -`supply` and `totalSupply` are distinct: `supply(denom)` returns the total supply of a single denomination as a `uint256`, while `totalSupply(pageKey)` returns a paginated list of every denomination's supply. - -The supporting structs are: - -```solidity -struct Coin { - uint256 amount; - string denom; -} - -struct SendEnabled { - string denom; - bool enabled; -} - -struct Params { - SendEnabled[] sendEnabled; - bool defaultSendEnabled; -} - -struct DenomUnit { - string denom; - uint32 exponent; - string[] aliases; -} - -struct Metadata { - string description; - DenomUnit[] denomUnits; - string base; - string display; - string name; - string symbol; -} -``` - -Methods that accept a `pageKey` argument are paginated. Pass empty bytes (`0x`) to request the first page, then pass the returned `nextKey` to fetch the next page. An empty `nextKey` means there are no more pages. - -```typescript -const account = '0x1234567890123456789012345678901234567890'; - -const [balances, nextKey] = await readBank.spendableBalances(account, '0x'); -for (const coin of balances) { - console.log(`${coin.amount} ${coin.denom}`); -} - -const seiSupply = await readBank.supply('usei'); -console.log(`${ethers.formatUnits(seiSupply, 6)} SEI total supply`); - -const metadata = await readBank.denomMetadata('usei'); -console.log(metadata.name, metadata.symbol); -``` - - ## Setup ```bash @@ -226,3 +136,73 @@ contract NativeSeiSender { ``` Validate the recipient before you send SEI. A successful transaction cannot be reversed. + +## Other query methods + +Since v6.7.0, the Bank precompile also has read-only queries for spendable balances, total supply, Bank Module parameters, and denomination metadata. + +```solidity +function supply(string memory denom) external view returns (uint256 response); +function spendableBalances(address acc, bytes memory pageKey) + external view returns (Coin[] memory balances, bytes memory nextKey); +function totalSupply(bytes memory pageKey) + external view returns (Coin[] memory supply, bytes memory nextKey); +function params() external view returns (Params memory params); +function denomMetadata(string memory denom) external view returns (Metadata memory metadata); +function denomsMetadata(bytes memory pageKey) + external view returns (Metadata[] memory metadatas, bytes memory nextKey); + +struct Coin { + uint256 amount; + string denom; +} + +struct SendEnabled { + string denom; + bool enabled; +} + +struct Params { + SendEnabled[] sendEnabled; + bool defaultSendEnabled; +} + +struct DenomUnit { + string denom; + uint32 exponent; + string[] aliases; +} + +struct Metadata { + string description; + DenomUnit[] denomUnits; + string base; + string display; + string name; + string symbol; +} +``` + +`supply(denom)` returns the supply of one denomination. `totalSupply(pageKey)` returns the supply of every denomination, one page at a time. For paginated methods, pass empty bytes (`0x`) for the first page, then pass the returned `nextKey`. An empty `nextKey` means there are no more pages. + +`BANK_PRECOMPILE_ABI` in `@sei-js/precompiles@3` does not include `spendableBalances`, `totalSupply`, `params`, `denomMetadata`, or `denomsMetadata`. Call them with an ABI fragment: + +```typescript +const bankQueries = new ethers.Contract( + BANK_PRECOMPILE_ADDRESS, + [ + 'function supply(string denom) view returns (uint256)', + 'function spendableBalances(address acc, bytes pageKey) view returns ((uint256 amount, string denom)[] balances, bytes nextKey)', + ], + provider, +); + +const supplyUsei = await bankQueries.supply('usei'); +console.log(`${ethers.formatUnits(supplyUsei, 6)} SEI total supply`); + +const account = '0x1234567890123456789012345678901234567890'; +const [balances, nextKey] = await bankQueries.spendableBalances(account, '0x'); +for (const coin of balances) { + console.log(`${coin.amount} ${coin.denom}`); +} +``` diff --git a/evm/precompiles/distribution.mdx b/evm/precompiles/distribution.mdx index 8e1eb52..33f3a1c 100644 --- a/evm/precompiles/distribution.mdx +++ b/evm/precompiles/distribution.mdx @@ -260,14 +260,12 @@ require(success, "Failed to withdraw commission"); Returns the reward information for a delegator across all validators. - - -**Read-only as of v6.7.0 (#)**: The distribution reward query — including this `rewards()` precompile, the gRPC `DelegationRewards`/`DelegationTotalRewards` queries, and the legacy querier — is read-only and idempotent as of v6.7.0. It computes outstanding rewards without mutating validator-period state, so repeated calls return identical results and leave distribution state unchanged. Before v6.7.0 the query incremented the validator period on each call. The old mutating behavior is reproduced only when re-tracing pre-v6.7.0 blocks via `debug_trace*`. - ```solidity function rewards(address delegatorAddress) external view returns (Rewards rewards); ``` +Since v6.7.0, `rewards()` and the matching gRPC queries (`DelegationRewards` and `DelegationTotalRewards`) do not change distribution state. Repeated calls return the same result. Before v6.7.0, each call incremented the validator period. `debug_trace*` keeps the old behavior when it replays blocks from before v6.7.0. + **Data structures:** ```solidity diff --git a/evm/precompiles/evidence.mdx b/evm/precompiles/evidence.mdx index 48e4cb3..dcb6ea5 100644 --- a/evm/precompiles/evidence.mdx +++ b/evm/precompiles/evidence.mdx @@ -1,21 +1,16 @@ --- -title: Evidence Precompile -description: Query the evidence module from the EVM using the Evidence precompile. +title: 'Evidence Precompile' +sidebarTitle: 'Evidence' +description: 'Read evidence of validator misbehavior from EVM contracts with the Evidence precompile.' +keywords: ['evidence precompile', 'equivocation', 'validator misbehavior', 'sei evm'] --- +**Address:** `0x000000000000000000000000000000000000100F` -The Evidence precompile exposes the Cosmos `evidence` module's read-only queries to EVM smart contracts. It lets you look up a single piece of evidence by its hash or page through all recorded evidence. Both methods are views and never mutate state. - -## Address - -The Evidence precompile is located at the following address: - -``` -0x000000000000000000000000000000000000100F -``` +The Evidence precompile reads the evidence of validator misbehavior that the Cosmos `evidence` module recorded. All methods are `view` functions. The precompile is available since v6.7.0. ## Interface -```solidity +```solidity Evidence.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -24,14 +19,9 @@ address constant EVIDENCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000 IEvidence constant EVIDENCE_CONTRACT = IEvidence(EVIDENCE_PRECOMPILE_ADDRESS); interface IEvidence { - // Queries - // Returns the JSON encoding of the evidence with the given hash. function evidence(bytes memory evidenceHash) external view returns (bytes memory); - - // Returns all evidence, each entry JSON-encoded, with pagination. function allEvidence(bytes memory pageKey) external view returns (AllEvidenceResponse memory response); - // Structs struct AllEvidenceResponse { bytes[] evidenceList; bytes nextKey; @@ -41,45 +31,12 @@ interface IEvidence { ## Methods -### `evidence` - -Returns the evidence recorded under the given hash, JSON-encoded as bytes. The call reverts if no evidence exists for the provided hash. - -```solidity -function evidence(bytes memory evidenceHash) external view returns (bytes memory); -``` - -**Parameters** - -- `evidenceHash` — the hash of the evidence to look up. - -**Returns** - -- A `bytes` value containing the JSON encoding of the evidence (for example an `Equivocation` record with its `@type`, height, power, time and consensus address fields). - -### `allEvidence` - -Returns all recorded evidence, paginated. Each entry in `evidenceList` is the JSON encoding of a single piece of evidence. - -```solidity -function allEvidence(bytes memory pageKey) external view returns (AllEvidenceResponse memory response); -``` - -**Parameters** - -- `pageKey` — the pagination key returned by a previous call. Pass empty bytes (`""`) to request the first page. - -**Returns** - -- `AllEvidenceResponse` containing: - - `evidenceList` — an array of JSON-encoded evidence entries. - - `nextKey` — the pagination key for the next page, or empty bytes when the results are exhausted. - -## Structs +| Method | Returns | +| --- | --- | +| `evidence(evidenceHash)` | The evidence with this hash. The call reverts if no evidence has this hash. | +| `allEvidence(pageKey)` | All evidence, one page at a time. | -### `AllEvidenceResponse` +## Notes -| Field | Type | Description | -| -------------- | --------- | ------------------------------------------------------ | -| `evidenceList` | `bytes[]` | JSON-encoded evidence entries for the current page. | -| `nextKey` | `bytes` | Pagination key for the next page (empty when exhausted).| +- The precompile encodes each piece of evidence as JSON. The JSON includes an `@type` field and the fields of the record, such as the height and consensus address of an `Equivocation` record. +- For `allEvidence()`, pass empty bytes for the first page, then pass the returned `nextKey`. An empty `nextKey` means there are no more pages. diff --git a/evm/precompiles/example-usage.mdx b/evm/precompiles/example-usage.mdx index 5d336f7..d6f7a9b 100644 --- a/evm/precompiles/example-usage.mdx +++ b/evm/precompiles/example-usage.mdx @@ -22,23 +22,22 @@ Sei precompiles are special smart contracts deployed at fixed addresses. They ex | Pointer view | `0x000000000000000000000000000000000000100A` | Look up pointer contract addresses | | Pointer | `0x000000000000000000000000000000000000100B` | Register pointer contracts | | Solo | `0x000000000000000000000000000000000000100C` | Claim Solo migration payloads | -| Auth | `0x000000000000000000000000000000000000100D` | Query accounts, account params, and the next account number | -| Authz | `0x000000000000000000000000000000000000100E` | Query authz grants by granter and grantee | -| Evidence | `0x000000000000000000000000000000000000100F` | Query misbehaviour evidence | -| Feegrant | `0x0000000000000000000000000000000000001010` | Query fee allowances | +| Auth | `0x000000000000000000000000000000000000100D` | Query accounts, auth parameters, and the next account number | +| Authz | `0x000000000000000000000000000000000000100E` | Query authz grants | +| Evidence | `0x000000000000000000000000000000000000100F` | Query evidence of validator misbehavior | | P256 | `0x0000000000000000000000000000000000001011` | Verify P-256 elliptic curve signatures | -| Mint | `0x0000000000000000000000000000000000001012` | Query the mint params and minter state | -| Params | `0x0000000000000000000000000000000000001013` | Query module params by subspace and key | -| Slashing | `0x0000000000000000000000000000000000001014` | Query slashing params and validator signing info, and unjail a validator via `unjail()` | -| Upgrade | `0x0000000000000000000000000000000000001015` | Query the current/applied upgrade plan and module versions | +| Mint | `0x0000000000000000000000000000000000001012` | Query mint parameters and the minter state | +| Params | `0x0000000000000000000000000000000000001013` | Query module parameters by subspace and key | +| Slashing | `0x0000000000000000000000000000000000001014` | Query slashing parameters and signing info, and unjail validators | +| Upgrade | `0x0000000000000000000000000000000000001015` | Query upgrade plans and module versions | -This page shows Bank, Staking, Governance, Distribution, JSON, and Solo. The other precompiles have their own pages: [CosmWasm](/evm/precompiles/cosmwasm-precompiles/cosmwasm), [Address](/evm/precompiles/cosmwasm-precompiles/addr), [Pointer contracts](/evm/evm-parity/examples/pointer-contracts) for both pointer precompiles, and [P256](/evm/precompiles/p256-precompile). +This page shows Bank, Staking, Governance, Distribution, JSON, and Solo. The other precompiles have their own pages: [CosmWasm](/evm/precompiles/cosmwasm-precompiles/cosmwasm), [Address](/evm/precompiles/cosmwasm-precompiles/addr), [Pointer contracts](/evm/evm-parity/examples/pointer-contracts) for both pointer precompiles, [P256](/evm/precompiles/p256-precompile), [Auth](/evm/precompiles/auth), [Authz](/evm/precompiles/authz), [Evidence](/evm/precompiles/evidence), [Mint](/evm/precompiles/mint), [Params](/evm/precompiles/params), [Slashing](/evm/precompiles/slashing), and [Upgrade](/evm/precompiles/upgrade). The `@sei-js/precompiles` package does not export the IBC and Oracle precompiles. IBC is disabled in both directions, and native Oracle queries are retired. Calls to either precompile cannot succeed on live Sei. See [IBC is disabled](/learn/sip-03-migration#ibc-is-disabled) and [Oracle Precompile (Retired)](/evm/precompiles/oracle). -`@sei-js/precompiles@3` is ESM-only and matches the Sei Chain v6.6.1 ABIs. Import raw `*_PRECOMPILE_ABI` constants from the package root, `@sei-js/precompiles/precompiles`, or `@sei-js/precompiles/viem`. Import the ethers factories from `@sei-js/precompiles/ethers`. The package does not expose `VIEM_*_PRECOMPILE_ABI` aliases. It re-exports the viem `sei` and `seiTestnet` definitions and also exports `seiLocal` (chain ID `713714`, `http://localhost:8545`). The package requires viem 2.55.16 or newer. +`@sei-js/precompiles@3` is ESM-only and matches the Sei Chain v6.6.1 ABIs. It does not include the Auth, Authz, Evidence, Mint, Params, Slashing, and Upgrade precompiles, or the query methods that v6.7 added to Bank and Governance. For those, compile the Solidity interface on each precompile's page to get the ABI. Import raw `*_PRECOMPILE_ABI` constants from the package root, `@sei-js/precompiles/precompiles`, or `@sei-js/precompiles/viem`. Import the ethers factories from `@sei-js/precompiles/ethers`. The package does not expose `VIEM_*_PRECOMPILE_ABI` aliases. It re-exports the viem `sei` and `seiTestnet` definitions and also exports `seiLocal` (chain ID `713714`, `http://localhost:8545`). The package requires viem 2.55.16 or newer. ```ts import { getStakingPrecompileEthersV6Contract } from '@sei-js/precompiles/ethers'; diff --git a/evm/precompiles/feegrant.mdx b/evm/precompiles/feegrant.mdx deleted file mode 100644 index 018a5c0..0000000 --- a/evm/precompiles/feegrant.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: Feegrant Precompile -description: Query fee allowances granted on Sei directly from EVM smart contracts using the Feegrant precompile. ---- - -The Feegrant precompile exposes read-only queries over the Cosmos `feegrant` module, letting EVM contracts inspect the fee allowances that granters have issued to grantees. - -## Address - -The Feegrant precompile is located at the fixed address: - -``` -0x0000000000000000000000000000000000001010 -``` - -## Interface - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -address constant FEEGRANT_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001010; - -IFeegrant constant FEEGRANT_CONTRACT = IFeegrant(FEEGRANT_PRECOMPILE_ADDRESS); - -interface IFeegrant { - // Queries - // Returns the fee allowance granted to the grantee by the granter. The - // allowance field of the returned grant is JSON-encoded. - function allowance(address granter, address grantee) external view returns (Grant memory grant); - - // Returns all the fee allowances granted to the given grantee, with pagination. - function allowances(address grantee, bytes memory pageKey) external view returns (AllowancesResponse memory response); - - // Returns all the fee allowances issued by the given granter, with pagination. - function allowancesByGranter(address granter, bytes memory pageKey) external view returns (AllowancesResponse memory response); - - // Structs - struct Grant { - string granter; - string grantee; - bytes allowance; - } - - struct AllowancesResponse { - Grant[] allowances; - bytes nextKey; - } -} -``` - -## Methods - -### `allowance` - -Returns the fee allowance granted to a grantee by a granter. - -```solidity -function allowance(address granter, address grantee) external view returns (Grant memory grant); -``` - -- `granter` — the granter's EVM address (must be associated with a Sei address). -- `grantee` — the grantee's EVM address (must be associated with a Sei address). - -The returned `Grant` contains the bech32 `granter` and `grantee` addresses, and the `allowance` field holds the grant's allowance encoded as JSON. The call reverts if no allowance exists between the two addresses, or if either address is not associated with a Sei address. - -### `allowances` - -Returns all fee allowances granted to the given grantee, with pagination. - -```solidity -function allowances(address grantee, bytes memory pageKey) external view returns (AllowancesResponse memory response); -``` - -- `grantee` — the grantee's EVM address (must be associated with a Sei address). -- `pageKey` — the pagination key from a previous response; pass empty bytes for the first page. - -The returned `AllowancesResponse` contains the list of `Grant` entries and a `nextKey` used to fetch the next page (empty when the results are exhausted). - -### `allowancesByGranter` - -Returns all fee allowances issued by the given granter, with pagination. - -```solidity -function allowancesByGranter(address granter, bytes memory pageKey) external view returns (AllowancesResponse memory response); -``` - -- `granter` — the granter's EVM address (must be associated with a Sei address). -- `pageKey` — the pagination key from a previous response; pass empty bytes for the first page. - -The returned `AllowancesResponse` follows the same shape as `allowances`. - -## Notes - -- All methods are view-only queries and must not be called with a non-zero `value`; doing so reverts. -- The `allowance` field of each `Grant` is JSON-encoded and includes a `@type` field identifying the allowance type (for example `BasicAllowance`). diff --git a/evm/precompiles/governance.mdx b/evm/precompiles/governance.mdx index cb3aaca..259f079 100644 --- a/evm/precompiles/governance.mdx +++ b/evm/precompiles/governance.mdx @@ -6,13 +6,13 @@ keywords: ['governance precompile', 'ethers.js', 'proposal voting', 'blockchain --- **Address:** `0x0000000000000000000000000000000000001006` -The Sei governance precompile lets EVM applications submit proposals, vote, and deposit tokens. The ABI is write-only. Query proposal state through Cosmos REST or RPC, or with a Sei explorer. +The Sei governance precompile lets EVM applications submit proposals, vote, deposit tokens, and read proposal state. **What is a precompile?** A precompile is a special smart contract that the Sei protocol itself deploys at a fixed address. It exposes custom native chain logic to EVM-based applications. To the EVM, it behaves like a regular contract, but it executes privileged, low-level logic efficiently. ## How does the governance precompile work? -The governance precompile at address `0x0000000000000000000000000000000000001006` exposes functions such as `vote()`, `voteWeighted()`, `deposit()`, and `submitProposal()`. +The governance precompile at address `0x0000000000000000000000000000000000001006` exposes transactions such as `vote()`, `voteWeighted()`, `deposit()`, and `submitProposal()`, and queries such as `proposal()` and `tallyResult()`. - EVM contracts and dApps can call governance functions like any other smart contract method. - Governance operations run natively at the chain level, which makes them efficient. @@ -101,10 +101,9 @@ function submitProposal( ) payable external returns (uint64 proposalID); ``` - ### Query functions -The governance precompile also exposes read-only query methods so you can read proposal state on-chain instead of relying on Cosmos REST or RPC. The vote and deposit queries are named `getVote` and `getDeposit` (rather than overloading the `vote`/`deposit` transaction methods) because ambiguous function names break common tooling such as ethers.js. +Use these read-only methods to read proposal state from a contract. The vote and deposit queries are `getVote` and `getDeposit`, because the transactions already use the names `vote` and `deposit`. Tools such as ethers.js do not handle overloaded function names well. ```solidity struct Coin { diff --git a/evm/precompiles/json.mdx b/evm/precompiles/json.mdx index 2f3b007..80726f9 100644 --- a/evm/precompiles/json.mdx +++ b/evm/precompiles/json.mdx @@ -713,7 +713,7 @@ async function handleMissingKeys(jsonPrecompile: ethers.Contract, data: any) { - **Boolean values:** Use the integer 0 for false and 1 for true. - **Key paths:** Extract parent objects first, then parse them manually. Dot notation may not be supported. - **Arrays:** Object-keyed arrays use `extractAsBytesList()`. JSON arrays by index use `extractAsBytesFromArray()`. -- **Gas costs:** The JSON precompile uses dynamic gas metering. It charges 100 gas per byte of the JSON payload (the first argument) against the call's gas meter, so larger JSON inputs consume proportionally more gas and need higher gas limits. +- **Gas costs:** The precompile charges 100 gas per byte of JSON input. Large JSON inputs need higher gas limits. - **Encoding:** Always use UTF-8 encoding with `ethers.toUtf8Bytes()`. - **Error handling:** Always implement fallback values for production applications. diff --git a/evm/precompiles/mint.mdx b/evm/precompiles/mint.mdx index e5e6d40..27a03d0 100644 --- a/evm/precompiles/mint.mdx +++ b/evm/precompiles/mint.mdx @@ -1,17 +1,12 @@ --- -title: Mint Precompile -description: Query the Sei mint module from EVM smart contracts to read inflation parameters and the current minter state. +title: 'Mint Precompile' +sidebarTitle: 'Mint' +description: 'Read the token release schedule and the current minter state from EVM contracts with the Mint precompile.' +keywords: ['mint precompile', 'token release schedule', 'minter', 'sei evm'] --- +**Address:** `0x0000000000000000000000000000000000001012` -The Mint precompile exposes read-only queries against the Sei mint module, letting EVM smart contracts inspect the chain's token release schedule and the current minter state. - -## Address - -The Mint precompile is located at the following address: - -``` -0x0000000000000000000000000000000000001012 -``` +The Mint precompile reads the Sei `mint` module: the token release schedule and the state of the current release period. All methods are `view` functions. The precompile is available since v6.7.0. ## Interface @@ -24,11 +19,9 @@ address constant MINT_PRECOMPILE_ADDRESS = 0x00000000000000000000000000000000000 IMint constant MINT_CONTRACT = IMint(MINT_PRECOMPILE_ADDRESS); interface IMint { - // Queries function params() external view returns (MintParams memory); function minter() external view returns (Minter memory); - // Structs struct ScheduledTokenRelease { string startDate; string endDate; @@ -55,59 +48,7 @@ interface IMint { ## Methods -All methods on the Mint precompile are read-only `view` functions. Calling them with a non-zero `value` reverts. - -### `params` - -Returns the mint module parameters, including the mint denom and the full scheduled token release schedule. - -```solidity -function params() external view returns (MintParams memory); -``` - -**Returns** - -- `MintParams` — a struct containing: - - `mintDenom` — the denomination minted by the module. - - `tokenReleaseSchedule` — an array of `ScheduledTokenRelease` entries, each with a `startDate`, `endDate`, and `tokenReleaseAmount`. - -### `minter` - -Returns the current minter state, describing the active release window and the amounts minted so far. - -```solidity -function minter() external view returns (Minter memory); -``` - -**Returns** - -- `Minter` — a struct containing: - - `startDate` — the start date of the current release period. - - `endDate` — the end date of the current release period. - - `denom` — the denomination being minted. - - `totalMintAmount` — the total amount to be minted over the current period. - - `remainingMintAmount` — the amount still to be minted in the current period. - - `lastMintAmount` — the amount minted in the most recent mint. - - `lastMintDate` — the date of the most recent mint. - - `lastMintHeight` — the block height of the most recent mint. - -## Usage Example - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "./Mint.sol"; - -contract MintReader { - function getMintDenom() external view returns (string memory) { - IMint.MintParams memory params = MINT_CONTRACT.params(); - return params.mintDenom; - } - - function getRemainingToMint() external view returns (uint64) { - IMint.Minter memory minter = MINT_CONTRACT.minter(); - return minter.remainingMintAmount; - } -} -``` +| Method | Returns | +| --- | --- | +| `params()` | The mint denomination and the token release schedule. Each release has a start date, an end date, and an amount. | +| `minter()` | The current release period. It includes the start and end dates, the denomination, the total amount to mint, and the amount left to mint. It also includes the amount, date, and block height of the last mint. | diff --git a/evm/precompiles/p256-precompile.mdx b/evm/precompiles/p256-precompile.mdx index 99b35d1..2f66dd7 100644 --- a/evm/precompiles/p256-precompile.mdx +++ b/evm/precompiles/p256-precompile.mdx @@ -18,7 +18,7 @@ The P256 precompile verifies signatures on the `secp256r1` curve efficiently. Ma - Various hardware security modules (HSMs) - Passkeys -This precompile implementation is significantly more gas efficient than Solidity-based implementations (up to 60x). +This precompile uses much less gas than a Solidity implementation of P-256 verification. ## Interface @@ -162,12 +162,9 @@ Total length: 160 bytes ## Gas costs -The precompile is much more gas efficient than Solidity implementations. Verification is a constant-work, stateless operation, so it is priced as a flat charge against the call's gas meter rather than per byte of input, matching RIP-7212's `P256VERIFY`: +Each verification costs a fixed 3,450 gas, the same as the RIP-7212 `P256VERIFY` precompile. The cost does not depend on the input size. It is much lower than the cost of P-256 verification in Solidity. -- **Total cost**: a fixed `P256VerifyGas = 3450` gas per verification -- **Efficiency**: Up to 60x more efficient than pure Solidity implementations - -The fixed verification cost is charged before the signature check runs. If the call is not funded for this cost, the resulting out-of-gas condition propagates and fails the transaction rather than being caught and downgraded to a reverted call. +The precompile charges this gas before it checks the signature. If the call cannot pay this gas, the call fails with an out-of-gas error. The call does not revert. ## Real-world use cases @@ -438,7 +435,7 @@ contract P256Test { ## Performance considerations -- **Gas efficiency**: 48,000 gas per verification, compared to 2M gas or more for Solidity implementations +- **Gas efficiency**: A verification costs a fixed 3,450 gas - **Batch operations**: Consider batching multiple verifications in a single transaction - **Caching**: Cache public keys on-chain to reduce calldata for repeated verifications - **Hardware integration**: Particularly efficient for applications that use hardware-backed keys diff --git a/evm/precompiles/params.mdx b/evm/precompiles/params.mdx index 305a3c7..ff762f8 100644 --- a/evm/precompiles/params.mdx +++ b/evm/precompiles/params.mdx @@ -1,17 +1,12 @@ --- -title: Params Precompile -description: Read on-chain module parameters from any x/params subspace through the Params precompile. +title: 'Params Precompile' +sidebarTitle: 'Params' +description: 'Read any module parameter by subspace and key from EVM contracts with the Params precompile.' +keywords: ['params precompile', 'module parameters', 'x/params', 'sei evm'] --- +**Address:** `0x0000000000000000000000000000000000001013` -The Params precompile exposes the Cosmos SDK `x/params` subspaces to the EVM, letting contracts read arbitrary module parameters by subspace and key. It is a read-only precompile deployed at a fixed address. - -## Address - -The Params precompile is available at the following address: - -``` -0x0000000000000000000000000000000000001013 -``` +The Params precompile reads module parameters from the Cosmos `x/params` subspaces. It has one `view` method. The precompile is available since v6.7.0. ## Interface @@ -24,46 +19,14 @@ address constant PARAMS_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000 IParams constant PARAMS_CONTRACT = IParams(PARAMS_PRECOMPILE_ADDRESS); interface IParams { - // Queries function params(string memory subspace, string memory key) external view returns (string memory value); } ``` ## Methods -### params - -Returns the value of the parameter identified by `subspace` and `key`. The value is returned as its JSON-encoded string representation. - -```solidity -function params( - string memory subspace, - string memory key -) external view returns (string memory value); -``` - -**Parameters** - -- `subspace` — the name of the params subspace to read from (e.g. `"staking"`). -- `key` — the parameter key within the subspace (e.g. `"MaxValidators"`). +| Method | Returns | +| --- | --- | +| `params(subspace, key)` | The value of the parameter `key` in `subspace`, as a JSON string. For example, `params("staking", "MaxValidators")` returns the maximum number of validators. | -**Returns** - -- `value` — the JSON-encoded value of the requested parameter. - -The call reverts if the subspace or key is unknown or empty. - -## Example Usage - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {IParams, PARAMS_CONTRACT} from "./Params.sol"; - -contract ParamsReader { - function getMaxValidators() external view returns (string memory) { - return PARAMS_CONTRACT.params("staking", "MaxValidators"); - } -} -``` +The call reverts if `subspace` or `key` is empty or unknown. diff --git a/evm/precompiles/slashing.mdx b/evm/precompiles/slashing.mdx index f4f0673..9c1636c 100644 --- a/evm/precompiles/slashing.mdx +++ b/evm/precompiles/slashing.mdx @@ -1,21 +1,16 @@ --- -title: "Slashing Precompile" -description: "Query the slashing module's parameters and validator signing information directly from the EVM." +title: 'Slashing Precompile' +sidebarTitle: 'Slashing' +description: 'Read slashing parameters and validator signing info, and unjail validators from EVM contracts with the Slashing precompile.' +keywords: ['slashing precompile', 'unjail', 'validator signing info', 'downtime', 'sei evm'] --- +**Address:** `0x0000000000000000000000000000000000001014` -The Slashing precompile exposes the Cosmos `slashing` module's read-only queries to EVM smart contracts. It lets you inspect the module parameters that govern downtime and double-sign penalties, and read per-validator signing information such as missed block counts and jailing status. - -## Address - -The Slashing precompile is available at the fixed address: - -``` -0x0000000000000000000000000000000000001014 -``` +The Slashing precompile reads data from the Cosmos `slashing` module and lets a validator operator unjail its validator. An operator can also let another account unjail the validator. The precompile is available since v6.7.0. ## Interface -```solidity +```solidity Slashing.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -24,18 +19,17 @@ address constant SLASHING_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000 ISlashing constant SLASHING_CONTRACT = ISlashing(SLASHING_PRECOMPILE_ADDRESS); interface ISlashing { + // Transactions + function unjail() external returns (bool success); + function grantUnjailAuthorization(address grantee, int64 expiration) external returns (bool success); + function unjailWithAuthorization(address validatorOperator) external returns (bool success); + function revokeUnjailAuthorization(address grantee) external returns (bool success); + // Queries function params() external view returns (SlashingParams memory params); + function signingInfo(string memory consAddress) external view returns (SigningInfo memory signingInfo); + function signingInfos(bytes memory pageKey) external view returns (SigningInfosResponse memory response); - function signingInfo( - string memory consAddress - ) external view returns (SigningInfo memory signingInfo); - - function signingInfos( - bytes memory pageKey - ) external view returns (SigningInfosResponse memory response); - - // Structs struct SlashingParams { int64 signedBlocksWindow; string minSignedPerWindow; @@ -62,117 +56,21 @@ interface ISlashing { ## Methods -### `params` - -```solidity -function params() external view returns (SlashingParams memory params); -``` - -Returns the slashing module parameters. - -**Returns** a `SlashingParams` struct: - -| Field | Type | Description | -| ------------------------- | -------- | --------------------------------------------------------------------------- | -| `signedBlocksWindow` | `int64` | The size of the sliding window used to track validator liveness. | -| `minSignedPerWindow` | `string` | Minimum fraction of blocks a validator must sign per window (decimal string).| -| `downtimeJailDuration` | `uint64` | Duration, in seconds, a validator is jailed for downtime. | -| `slashFractionDoubleSign` | `string` | Fraction of stake slashed for a double-sign (decimal string). | -| `slashFractionDowntime` | `string` | Fraction of stake slashed for downtime (decimal string). | - -### `signingInfo` - -```solidity -function signingInfo( - string memory consAddress -) external view returns (SigningInfo memory signingInfo); -``` - -Returns the signing information for a single validator identified by its consensus address (bech32). - -**Parameters** - -| Parameter | Type | Description | -| ------------- | -------- | ----------------------------------------- | -| `consAddress` | `string` | The validator's consensus address (bech32).| - -**Returns** a `SigningInfo` struct: - -| Field | Type | Description | -| --------------------- | -------- | ---------------------------------------------------------------- | -| `validatorAddress` | `string` | The validator's consensus address (bech32). | -| `startHeight` | `int64` | Height at which the validator became bonded. | -| `indexOffset` | `int64` | Index offset into the signed block bit array. | -| `jailedUntil` | `int64` | Unix timestamp (seconds) until which the validator is jailed. | -| `tombstoned` | `bool` | Whether the validator has been tombstoned. | -| `missedBlocksCounter` | `int64` | Number of blocks the validator has missed within the window. | - -Querying a consensus address with no signing information reverts. - - +### Queries -### `unjail` +| Method | Returns | +| --- | --- | +| `params()` | The slashing parameters. They include the signed-blocks window, the minimum fraction of blocks to sign, and the downtime jail duration in seconds. They also include the slash fractions for double signing and downtime. All fractions are decimal strings. | +| `signingInfo(consAddress)` | The signing info of the validator with this bech32 consensus address. It includes the start height, the index offset, and the jailed-until time as a Unix timestamp in seconds. It also includes the tombstone status and the missed-block count. The call reverts if the validator has no signing info. | +| `signingInfos(pageKey)` | The signing info of all validators, one page at a time. Pass empty bytes for the first page, then pass the returned `nextKey`. | -```solidity -function unjail() external returns (bool success); -``` - -Unjails the validator whose operator address is the caller's associated Sei address. This is a state-mutating transaction, so the caller's EVM address must be associated with a Sei address, and that Sei address must be a validator operator. - -**Returns** - -| Field | Type | Description | -| --------- | ------ | ---------------------------------------- | -| `success` | `bool` | `true` when the validator was unjailed. | - -The call reverts if the caller has no associated Sei address, if that address is not a validator operator, or if the validator does not currently satisfy the conditions required to be unjailed. - -Because this method acts on behalf of the caller, it is non-payable and cannot be invoked via `delegatecall` or `staticcall`; such calls revert. - -### `signingInfos` - -```solidity -function signingInfos( - bytes memory pageKey -) external view returns (SigningInfosResponse memory response); -``` - -Returns the signing information for all validators, with pagination. - -**Parameters** - -| Parameter | Type | Description | -| --------- | ------- | ---------------------------------------------------------------- | -| `pageKey` | `bytes` | Pagination key from a previous response; pass empty bytes for the first page.| +### Transactions -**Returns** a `SigningInfosResponse` struct: - -| Field | Type | Description | -| -------------- | --------------- | ------------------------------------------------------- | -| `signingInfos` | `SigningInfo[]` | The signing information entries for the current page. | -| `nextKey` | `bytes` | Pagination key for the next page; empty when exhausted. | - -## Example Usage - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "./Slashing.sol"; - -contract SlashingReader { - function getDowntimeSlashFraction() external view returns (string memory) { - ISlashing.SlashingParams memory params = SLASHING_CONTRACT.params(); - return params.slashFractionDowntime; - } - - function getMissedBlocks(string memory consAddress) external view returns (int64) { - ISlashing.SigningInfo memory info = SLASHING_CONTRACT.signingInfo(consAddress); - return info.missedBlocksCounter; - } -} -``` +| Method | Effect | +| --- | --- | +| `unjail()` | Unjails the validator whose operator address is the caller's associated Sei address. | +| `grantUnjailAuthorization(grantee, expiration)` | Lets `grantee` unjail the caller's validator until `expiration`, a Unix timestamp in seconds. | +| `unjailWithAuthorization(validatorOperator)` | Unjails the validator of `validatorOperator`, using a grant that `validatorOperator` gave the caller. | +| `revokeUnjailAuthorization(grantee)` | Removes the unjail grant that the caller gave `grantee`. | - - All methods on the Slashing precompile are read-only `view` queries. Sending value to any of them reverts. - +Each transaction returns `true` if it succeeds. Transactions do not accept value. A transaction reverts if you call it through `delegatecall` or `staticcall`. The caller and every address argument must have an associated Sei address. `unjail()` also reverts if the caller is not a validator operator, or if the validator does not meet the unjail conditions. diff --git a/evm/precompiles/upgrade.mdx b/evm/precompiles/upgrade.mdx index 6ef559c..dd42f3b 100644 --- a/evm/precompiles/upgrade.mdx +++ b/evm/precompiles/upgrade.mdx @@ -1,21 +1,16 @@ --- -title: Upgrade Precompile -description: Query the chain upgrade module from EVM smart contracts using the Upgrade precompile. +title: 'Upgrade Precompile' +sidebarTitle: 'Upgrade' +description: 'Read scheduled and applied chain upgrades and module consensus versions from EVM contracts with the Upgrade precompile.' +keywords: ['upgrade precompile', 'upgrade plan', 'module versions', 'sei evm'] --- +**Address:** `0x0000000000000000000000000000000000001015` -The Upgrade precompile exposes read-only queries against the chain's upgrade module to EVM smart contracts. It lets contracts inspect the currently scheduled upgrade plan, look up when a named upgrade was applied, read stored upgraded consensus state, and read the consensus versions of app modules. +The Upgrade precompile reads the Cosmos `upgrade` module: scheduled and applied upgrades, stored upgraded consensus state, and module consensus versions. All methods are `view` functions. The precompile is available since v6.7.0. -## Precompile Address +## Interface -The Upgrade precompile is available at the fixed address: - -``` -0x0000000000000000000000000000000000001015 -``` - -## Solidity Interface - -```solidity +```solidity Upgrade.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -24,30 +19,11 @@ address constant UPGRADE_PRECOMPILE_ADDRESS = 0x00000000000000000000000000000000 IUpgrade constant UPGRADE_CONTRACT = IUpgrade(UPGRADE_PRECOMPILE_ADDRESS); interface IUpgrade { - // Queries - // Returns the currently scheduled upgrade plan, or a zero-valued plan if - // no upgrade is scheduled. function currentPlan() external view returns (Plan memory plan); + function appliedPlan(string memory name) external view returns (int64 height); + function upgradedConsensusState(int64 lastHeight) external view returns (bytes memory consensusState); + function moduleVersions(string memory moduleName) external view returns (ModuleVersion[] memory versions); - // Returns the block height at which the named upgrade was applied, or 0 - // if it has not been applied. - function appliedPlan( - string memory name - ) external view returns (int64 height); - - // Returns the upgraded consensus state stored for the given last height, - // or empty bytes if none is stored. - function upgradedConsensusState( - int64 lastHeight - ) external view returns (bytes memory consensusState); - - // Returns the consensus versions of app modules. An empty moduleName - // returns all modules; a specific moduleName returns just that module. - function moduleVersions( - string memory moduleName - ) external view returns (ModuleVersion[] memory versions); - - // Structs struct Plan { string name; int64 height; @@ -63,70 +39,9 @@ interface IUpgrade { ## Methods -All methods are read-only `view` queries and are non-payable; sending value reverts the call. - -### currentPlan - -```solidity -function currentPlan() external view returns (Plan memory plan); -``` - -Returns the currently scheduled upgrade plan. If no upgrade is scheduled, a zero-valued `Plan` is returned (empty `name` and `info`, `height` of `0`). - -The returned `Plan` contains: - -- `name` — the name of the scheduled upgrade. -- `height` — the block height at which the upgrade is scheduled to run. -- `info` — arbitrary metadata attached to the plan. - -### appliedPlan - -```solidity -function appliedPlan(string memory name) external view returns (int64 height); -``` - -Returns the block height at which the upgrade identified by `name` was applied. If the upgrade has not been applied, it returns `0`. - -### upgradedConsensusState - -```solidity -function upgradedConsensusState(int64 lastHeight) external view returns (bytes memory consensusState); -``` - -Returns the upgraded consensus state stored for the given `lastHeight`. If no consensus state is stored for that height, it returns empty bytes. - -### moduleVersions - -```solidity -function moduleVersions(string memory moduleName) external view returns (ModuleVersion[] memory versions); -``` - -Returns the consensus versions of app modules. Passing an empty `moduleName` returns every module; passing a specific `moduleName` returns a single entry for that module. Querying an unknown module name reverts. - -Each `ModuleVersion` contains: - -- `name` — the module's name. -- `version` — the module's consensus version. - -## Example Usage - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {IUpgrade, UPGRADE_CONTRACT} from "./Upgrade.sol"; - -contract UpgradeReader { - function getCurrentPlan() external view returns (IUpgrade.Plan memory) { - return UPGRADE_CONTRACT.currentPlan(); - } - - function getAppliedHeight(string memory name) external view returns (int64) { - return UPGRADE_CONTRACT.appliedPlan(name); - } - - function getModuleVersions() external view returns (IUpgrade.ModuleVersion[] memory) { - return UPGRADE_CONTRACT.moduleVersions(""); - } -} -``` +| Method | Returns | +| --- | --- | +| `currentPlan()` | The scheduled upgrade plan: its name, height, and info. If there is no scheduled upgrade, all fields are empty or zero. | +| `appliedPlan(name)` | The block height where the chain applied the named upgrade, or `0` if the chain did not apply it. | +| `upgradedConsensusState(lastHeight)` | The stored upgraded consensus state for `lastHeight`, or empty bytes if there is none. | +| `moduleVersions(moduleName)` | The consensus version of the named module. Pass an empty string to get all modules. The call reverts if the module is unknown. | diff --git a/evm/reference.mdx b/evm/reference.mdx index d104806..8134a94 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -15,7 +15,7 @@ Sei fully supports the standard Ethereum JSON-RPC API, so existing EVM tools (et By default, the explorer lists only the current methods that are available. Each method has one of three status labels: - **Supported**: The method is registered and has a full implementation with standard Ethereum behavior. -- **Limited**: The method is callable, but it returns a static value or has a Sei-specific caveat (shown in the method's **Sei-specific behavior** note). For example, `eth_coinbase` returns the fee-collector address, and `eth_getProof` returns an IAVL proof. +- **Limited**: The method is callable, but it returns a static value or has a Sei-specific caveat (shown in the method's **Sei-specific behavior** note). For example, `eth_coinbase` returns the fee-collector address. - **Unavailable**: The method is not registered, returns an explicit error, or does not apply to Sei's architecture. This includes blob methods (`eth_blobBaseFee`), proof-of-work and uncle methods, and the `admin`, `miner`, `clique`, `engine`, `les`, and `personal` namespaces. By default, the explorer hides the remaining deprecated `sei_*` extensions and the unavailable methods. To browse them, enable **Show deprecated & unavailable methods**. The deprecated extensions are also documented under [Sei custom endpoints](#sei-custom-endpoints). @@ -108,13 +108,13 @@ Because `FinalizeBlock` responses are not stored on disk under Autobahn, `/block ### Filter and subscription limits -By default, log filters (`eth_getLogs`, `eth_getFilterLogs`) have these limits: +By default, log filters (`eth_getLogs`, `eth_getFilterLogs`) have these limits. Operators set them in the `[evm]` section of `app.toml`. -- **Matched-log count:** a single query may match up to 10,000 logs (`max_log_no_block`, `evm.max_log_no_block`, default 10000). This cap applies to both bounded and open-ended block ranges. Exceeding it returns a `too many logs` error rather than a truncated result. -- **Estimated log bytes:** a single query may materialize up to 64 MiB of estimated heap bytes of matched logs (`max_log_bytes`, `evm.max_log_bytes`, default 67108864 / 64 MiB). Exceeding it returns a `too many log bytes` error. A non-positive value falls back to the receipt-store default. -- **Close-ended block range:** up to 2,000 blocks in one query (`max_blocks_for_log`, `evm.max_blocks_for_log`, default 2000). +- **Matched logs:** a query can match up to 10,000 logs (`max_log_no_block`), for both bounded and open-ended block ranges. +- **Log data:** a query can load up to about 64 MiB of matched logs (`max_log_bytes`). +- **Closed block range:** a query can span up to 2,000 blocks (`max_blocks_for_log`). -When a query exceeds the matched-log count or the byte budget, the request now errors instead of silently truncating the result. Narrow the block range or the filter criteria (address and topics) and retry. +A query over the log count or log data limit fails with `query matches too many logs` or `query matches too many log bytes`. It does not return partial results. Narrow the block range or the filters (address and topics), then try again. Real-time subscriptions (`eth_subscribe` for `newHeads` and `logs`) are available over WebSocket only. For transport details, see [WebSocket connections](/evm/evm-parity/websocket). @@ -130,11 +130,11 @@ You can also browse every method below in the interactive explorer above. This s **Sei-specific behavior:** Decodes the bytes to an ethtypes.Transaction, wraps it in a Cosmos MsgEVMTransaction, and broadcasts it through CometBFT. By default, it uses a CheckTx-level BroadcastTx (that is, `broadcast_tx_sync`), and slow mode uses BroadcastTxCommit. Non-zero CheckTx codes are returned as ABCI errors, not geth mempool errors. Legacy (non-1559) transactions must set gasPrice at or above the governance minimum (currently 50 gwei on Sei Mainnet). Blob (EIP-4844) transactions are not enabled. The method supports per-sender EvmProxy forwarding. -**Under Autobahn (`AutobahnConfigFile` set):** The node's role follows the top-level `mode` field in the CometBFT config. With `mode = "validator"`, the node runs the validator path and backs transaction submission with a local mempool; any other mode runs the node as a fullnode that loads the committee for routing only and has no local mempool. +**Under Autobahn (`AutobahnConfigFile` set):** The node runs as a validator when the top-level `mode` in `config.toml` is `"validator"`. With any other mode, it runs as a fullnode without a local mempool. -On a validator, transaction broadcast routes through Autobahn's producer-backed mempool instead of CometBFT's `TxMempool`. This mempool admits EVM transactions strictly in sequential nonce order for each sender. It rejects a transaction with a bad-nonce error if its nonce does not match the sender's next expected nonce. Senders must therefore submit nonces contiguously. +On a validator, transactions go to Autobahn's producer mempool instead of CometBFT's `TxMempool`. This mempool admits EVM transactions in strict nonce order for each sender. It rejects a transaction with a bad-nonce error if its nonce is not the sender's next expected nonce, so senders must submit nonces without gaps. -On an Autobahn fullnode, there is no local mempool. `eth_sendRawTransaction` is proxied to the shard owner (the committee member that owns the sender's EVM shard), which must have an EVM RPC URL configured — the `evmrpc` field is required for every committee member in the Autobahn config file (`autobahn.json`). The underlying Tendermint `broadcast_tx_*` methods (`broadcast_tx_async`, `broadcast_tx_sync`, `broadcast_tx_commit`) have no local backing on a fullnode and return the error `autobahn fullnode has no local mempool; broadcast_tx_* must be sent to a validator`. Submit transactions to a validator (or let `eth_sendRawTransaction`'s per-sender proxy forwarding handle the routing). Pending-nonce queries also behave differently: `eth_getTransactionCount` with the `pending` tag returns the on-chain confirmed nonce (the pending nonce lives on the shard owner), and `unconfirmed_txs` / `num_unconfirmed_txs` return empty. +On a fullnode, `eth_sendRawTransaction` forwards the transaction to the committee member that owns the sender's EVM shard. For this, every validator in `autobahn.json` must have an `evmrpc` URL. The Tendermint `broadcast_tx_async`, `broadcast_tx_sync`, and `broadcast_tx_commit` methods fail on a fullnode with `autobahn fullnode has no local mempool; broadcast_tx_* must be sent to a validator`. On a fullnode, `eth_getTransactionCount` with the `pending` tag returns the confirmed nonce, and `unconfirmed_txs` and `num_unconfirmed_txs` return empty results. Under Autobahn, the default CheckTx-synchronous broadcast (`broadcast_tx_sync`, the same path named above) blocks while the mempool is full. It returns only after capacity is available. As a result, a default `eth_sendRawTransaction` call can stall for as long as the mempool stays full. The async path (`broadcast_tx_async`) may silently drop the transaction instead. The `unsafe_flush_mempool` Tendermint RPC endpoint is not supported under Autobahn and returns `unsafe_flush_mempool is not supported with autobahn mempool`. @@ -544,39 +544,6 @@ If a receipt exists but its block height is above the safe-latest watermark, the } ``` -#### `eth_getProof` - -**Limited.** Returns a Merkle proof for an account and the requested storage slots. - -**Sei-specific behavior:** Sei stores state in an IAVL-style tree, not an Ethereum Merkle-Patricia trie. The handler unwraps the EVM store through any intervening wrappers (cache, tracing, Giga cache, prefix stores). It continues until it reaches an underlying proof-capable queryable store: classic IAVL, a store/v2 memiavl commitment, or any other proof-capable root. Proofs therefore work across these backends, not only on classic IAVL. If the handler cannot reach a proof-capable queryable store, it returns `cannot find a proof-capable queryable KV store`. - -The result is a Sei-specific ProofResult{address, hexValues, storageProof}. Its storageProof entries are CometBFT/IAVL crypto.ProofOps, not eth-style MPT proof nodes. The result has no accountProof, balance, codeHash, nonce, or storageHash field (Sei has no per-account state root). Standard `eth_getProof` verifiers do not work with it. - -**Parameters:** - -| # | Name | Type | Description | -| :- | :- | :- | :- | -| 1 | `address` | DATA, 20 bytes | Account address. | -| 2 | `storageKeys` | array of DATA, 32 bytes | Storage slot keys to prove (bounded by MaxStorageKeysPerProof). | -| 3 | `blockNrOrHash` | BLOCKNUMBER or DATA | Block number, tag, or hash. | - -**Example request:** - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "eth_getProof", - "params": [ - "0xE30feDd158A2e3b13e9badaeABaFc5516e95e8C7", - [ - "0x0" - ], - "latest" - ] -} -``` - #### `eth_accounts` **Limited.** Returns the list of addresses for which the node holds hosted keys. @@ -966,7 +933,7 @@ Heights with pruned or partial base-fee data are skipped in `baseFeePerGas`, but **Limited.** Opens a WebSocket-only push subscription for newHeads or logs notifications. -**Sei-specific behavior:** Available over WebSocket only. SubscriptionAPI is not registered on the HTTP server, so over HTTP the method returns rpc.ErrNotificationsUnsupported. Only 'newHeads' and 'logs' are implemented in source. There is no 'newPendingTransactions' subscription, although some client docs imply otherwise. The number of concurrent subscriptions is capped per connection: `max_subscriptions_new_head` (`evm.max_subscriptions_new_head`) caps `newHeads` subscriptions, and `max_subscriptions_logs` (`evm.max_subscriptions_logs`, default 1000) caps concurrent `logs` subscriptions. Once the logs cap is reached, a new `logs` subscription is rejected with `no new subscription can be created`. +**Sei-specific behavior:** Available over WebSocket only. SubscriptionAPI is not registered on the HTTP server, so over HTTP the method returns rpc.ErrNotificationsUnsupported. Only 'newHeads' and 'logs' are implemented in source. There is no 'newPendingTransactions' subscription, although some client docs imply otherwise. The node limits concurrent subscriptions across all connections: up to 10,000 `newHeads` subscriptions (`max_subscriptions_new_head`) and up to 1,000 `logs` subscriptions (`max_subscriptions_logs`). When the node reaches a limit, a new subscription fails with `no new subscription can be created`. **Parameters:** @@ -1037,7 +1004,7 @@ The `filter` object applies only to `logs` subscriptions. For `newHeads`, pass t | 3 | `overrides` | object | Optional per-account state overrides (balance, code, nonce, state). | | 4 | `blockOverrides` | object | Optional block-context overrides (number, time, coinbase). | -State overrides are bounded in size. `max_state_override_accounts` (`evm.max_state_override_accounts`, default 100) caps the number of accounts in a single override, and `max_state_override_slots` (`evm.max_state_override_slots`, default 1000) caps the number of storage slots per account (applied to both `state` and `stateDiff`). Requests that exceed either cap are rejected with a 'state override has too many accounts' or 'too many slots' error. Setting a cap to 0 disables that limit. These caps apply equally to `eth_estimateGas`, `eth_estimateGasAfterCalls`, and `debug_traceCall`. +A state override can include up to 100 accounts (`max_state_override_accounts`) and up to 1,000 storage slots per account in `state` or `stateDiff` (`max_state_override_slots`). A larger override fails with a `too many accounts` or `too many slots` error. An operator can set a limit to `0` to turn it off. The same limits apply to `eth_estimateGas`, `eth_estimateGasAfterCalls`, and `debug_traceCall`. **Example request:** @@ -1091,7 +1058,7 @@ State overrides are bounded in size. `max_state_override_accounts` (`evm.max_sta **Limited.** Estimates gas for a transaction after it applies a sequence of preceding calls to the same simulated state. -**Sei-specific behavior:** This is a non-standard Sei/geth extension (not part of the standard Ethereum JSON-RPC spec). It has the same gas-cap and fail-fast-limiter behavior as `eth_estimateGas`. The number of preceding `calls` is capped by the `evm.max_estimate_gas_calls` config (default 100). A request whose `calls` array exceeds this limit is rejected early with a 'too many calls' error before any state wrapping or resource acquisition. +**Sei-specific behavior:** This is a non-standard Sei/geth extension (not part of the standard Ethereum JSON-RPC spec). It has the same gas-cap and fail-fast-limiter behavior as `eth_estimateGas`. A request can include up to 100 calls to apply first (`max_estimate_gas_calls`). A request with more calls fails with a `too many calls` error. **Parameters:** @@ -1124,7 +1091,7 @@ State overrides are bounded in size. `max_state_override_accounts` (`evm.max_sta **Supported.** Generates an EIP-2930 access list (and gas used) for a transaction. -**Sei-specific behavior:** Defaults to the pending block tag, as geth does. If a VM error occurs during simulation, the method reports it in the result's 'error' field instead of failing the RPC. A fail-fast request limiter may reject the call with 'eth_createAccessList rejected due to rate limit: server busy' when the limiter is saturated, as with `eth_call`, `eth_estimateGas`, and `eth_estimateGasAfterCalls`. +**Sei-specific behavior:** Defaults to the pending block tag, as geth does. If a VM error occurs during simulation, the method reports it in the result's 'error' field instead of failing the RPC. When the node's request limiter is full, the call fails with `eth_createAccessList rejected due to rate limit: server busy`, like `eth_call` and `eth_estimateGas` do. **Parameters:** @@ -1168,9 +1135,9 @@ State overrides are bounded in size. `max_state_override_accounts` (`evm.max_sta **Supported.** Replays a transaction by hash and returns an execution trace from the configured tracer. -**Sei-specific behavior:** Available over HTTP only, because the debug namespace is not registered on the WebSocket server. Supports geth tracers: callTracer, prestateTracer, flatCallTracer, and the struct (opcode) logger. TraceBaker pre-bakes and caches the results of callTracer, prestateTracer, and flatCallTracer. Requires trace-enabled or archive state for the target height. +**Sei-specific behavior:** Available over HTTP only, because the debug namespace is not registered on the WebSocket server. Supports native geth tracers such as callTracer, prestateTracer, and flatCallTracer, and the struct (opcode) logger. TraceBaker pre-bakes and caches the results of callTracer, prestateTracer, and flatCallTracer. Requires trace-enabled or archive state for the target height. -**Tracer gating.** Caller-supplied `TraceConfig.Tracer` names are gated by `[evm]` config in `app.toml` (#3618). Only native tracer names listed in `trace_allowed_tracers` (`evm.trace_allowed_tracers`, default `["callTracer", "prestateTracer", "flatCallTracer", "4byteTracer", "noopTracer", "muxTracer"]`) may be requested; a name that is not in the list is rejected. Request-supplied JavaScript tracer source is rejected unless `trace_allow_js_tracers` (`evm.trace_allow_js_tracers`, default `false`) is enabled, and enabling it does not widen the native allowlist. When `muxTracer` is requested, its nested tracer names are validated recursively (bounded depth of 16). Omitting `tracer` uses the default struct logger, which is always available. This gating applies consistently across `debug_traceTransaction`, `debug_traceCall`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceTransactionProfile`. +**Tracer rules.** The node accepts only the native tracers listed in `trace_allowed_tracers`. By default, these are `callTracer`, `prestateTracer`, `flatCallTracer`, `4byteTracer`, `noopTracer`, and `muxTracer`. The node rejects JavaScript tracer code unless the operator sets `trace_allow_js_tracers = true`. If you omit `tracer`, the default struct logger runs. These rules apply to every `debug_trace*` method that accepts a tracer. For details, see [Tracer allowlist](/evm/tracing#tracer-allowlist). The max block lookback guard (`max_trace_lookback_blocks`) applies. If a request's target block is older than the configured lookback, the request is rejected with an error of the form `block number X is beyond max lookback of Y`. Each such request increments the `evmrpc_historical_debug_trace_attempts_total` metric. This guard applies consistently across all `debug_trace*` endpoints. @@ -1255,38 +1222,11 @@ The max block lookback guard (`max_trace_lookback_blocks`) applies. If a request } ``` -#### `debug_traceStateAccess` - -**Supported.** Replays a transaction by hash and returns the per-module state (KVStore) accesses it made, used for prestate derivation and state-access analysis. - -**Sei-specific behavior:** Available over HTTP only. The `max_trace_lookback_blocks` historical guard applies (see `debug_traceTransaction`). The call honors the configured `TraceTimeout`: replay and the subsequent prestate/trace serialization bail out when the trace deadline elapses, failing with a `context deadline exceeded` error instead of running unbounded. This prevents long-running replays from consuming resources without limit. - -Each module entry in the response carries a `truncated` boolean. Sei caps the key/value payload retained per module in the state-access log at 4 MiB. Once that cap is reached, the module's log is marked `truncated: true` and further accesses are dropped from the retained log, bounding a pathological transaction (such as a huge iterator scan) that would otherwise grow the response without limit. When a module is truncated, its `reads` and `has` entries reflect only the retained prefix, but its `stats` (operation counts and durations) stay complete and accurate, because they are accumulated at record time regardless of truncation. - -**Parameters:** - -| # | Name | Type | Description | -| :- | :- | :- | :- | -| 1 | `hash` | DATA, 32 bytes | Transaction hash to trace. | - -**Example request:** - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "debug_traceStateAccess", - "params": [ - "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060" - ] -} -``` - #### `debug_traceCall` **Supported.** Executes and traces a call against a block's state without creating a transaction. -**Sei-specific behavior:** Available over HTTP only. Arbitrary geth tracer names pass through. Tracing on the pending block is not supported. The `max_trace_lookback_blocks` historical guard applies (see `debug_traceTransaction`). +**Sei-specific behavior:** Available over HTTP only. The tracer must be on the node's allowlist (see the tracer rules under `debug_traceTransaction`). Tracing on the pending block is not supported. The `max_trace_lookback_blocks` historical guard applies (see `debug_traceTransaction`). **Parameters:** @@ -1320,7 +1260,9 @@ Each module entry in the response carries a `truncated` boolean. Sei caps the ke **Limited.** Sei extension that replays a transaction and returns its app/tendermint/receipt state-access traces. -**Sei-specific behavior:** This Sei-specific extension is not part of the upstream go-ethereum debug namespace. It is available over HTTP only and subject to the availability guards for historical debug traces. The `max_trace_lookback_blocks` historical guard applies (see `debug_traceTransaction`). +**Sei-specific behavior:** This Sei-specific extension is not part of the upstream go-ethereum debug namespace. It is available over HTTP only and subject to the availability guards for historical debug traces. The `max_trace_lookback_blocks` historical guard applies (see `debug_traceTransaction`). The call fails with `context deadline exceeded` when `trace_timeout` elapses. + +Sei keeps at most 4 MiB of key and value data per module in the access log. A module that goes over this limit has `"truncated": true`. Its `reads` and `has` lists then hold only the first accesses, but its `stats` (operation counts and durations) stay complete. **Parameters:** @@ -1412,15 +1354,15 @@ The response `result` contains a `trace` field (the standard tracer output) and ## Sei custom endpoints -Sei keeps four legacy custom endpoints for address resolution, cross-VM transaction lookup, and synthetic receipts. +Sei keeps three legacy custom endpoints for address resolution and cross-VM transaction lookup. -For new integrations, use standard `eth_*` and `debug_*` methods. The four remaining `sei_*` methods are deprecated and scheduled for removal. +For new integrations, use standard `eth_*` and `debug_*` methods. The three remaining `sei_*` methods are deprecated and scheduled for removal. -The `sei2_*` namespace has been removed. Most legacy `sei_*` methods have also been removed, including block, filter, log, signing, association, and transaction lookup methods. The `sei_*` cleanup is tracked in [sei-chain PR #3927](https://github.com/sei-protocol/sei-chain/pull/3927). +The `sei2_*` namespace has been removed. Most legacy `sei_*` methods have also been removed, including block, filter, log, signing, association, and transaction lookup methods. The `sei_*` cleanup is tracked in [sei-chain PR #3927](https://github.com/sei-protocol/sei-chain/pull/3927). Since v6.7.0, `sei_getTransactionReceipt` is also removed. -No remaining block or filter method can discover synthetic logs from Cosmos-originated transactions. If you already know a synthetic transaction hash, enable `sei_getTransactionReceipt` to fetch its receipt and logs. +No RPC method returns receipts or logs for synthetic transactions from Cosmos-originated messages. `eth_getTransactionReceipt` returns receipts for EVM transactions only. The `enabled_legacy_sei_apis` setting under `[evm]` in `app.toml` controls access. Only methods explicitly listed in this allowlist are available. Disabled methods return a standard JSON-RPC error (code `-32601`, data `"legacy_sei_deprecated"`). Allowed single-object calls pass through unchanged, with an optional `Sei-Legacy-RPC-Deprecation` HTTP response header that signals deprecation. @@ -1438,20 +1380,16 @@ enabled_legacy_sei_apis = [ "sei_getSeiAddress", "sei_getEVMAddress", "sei_getCosmosTx", - - # Optional: enable synthetic receipt lookup. - # "sei_getTransactionReceipt", ] ``` -Only these four methods are available: +Only these three methods are available. The node ignores any other name in the list. | Method | Description | | ------ | ----------- | | `sei_getSeiAddress` | Returns the Sei address associated with an EVM address. | | `sei_getEVMAddress` | Returns the EVM address associated with a Sei address. | | `sei_getCosmosTx` | Returns the wrapping Cosmos transaction hash as uppercase hexadecimal without a `0x` prefix. | -| `sei_getTransactionReceipt` | Returns a receipt, including a synthetic receipt when its transaction hash is known. | ### Address resolution @@ -1537,7 +1475,7 @@ Returns the wrapping Cosmos transaction hash for a given EVM transaction hash. ### Legacy deprecation error -If a client calls a remaining `sei_*` method that is not listed in `enabled_legacy_sei_apis`, the node returns: +If a client calls a remaining `sei_*` method that is not listed in `enabled_legacy_sei_apis`, the node returns the error below. The node returns the same error for removed `sei_*` methods, such as `sei_getTransactionReceipt`. If you add a removed method to `enabled_legacy_sei_apis`, the method stays unavailable. ```json { @@ -1545,7 +1483,7 @@ If a client calls a remaining `sei_*` method that is not listed in `enabled_lega "id": 1, "error": { "code": -32601, - "message": "sei_getTransactionReceipt is not enabled on this node. The sei_* JSON-RPC surface is deprecated, scheduled for removal, and should not be used for new integrations - prefer standard eth_* (and debug_*) methods and official migration guidance. To allow this legacy method, add it to enabled_legacy_sei_apis under [evm] in app.toml.", + "message": "sei_getCosmosTx is not enabled on this node. The sei_* JSON-RPC surface is deprecated, scheduled for removal, and should not be used for new integrations - prefer standard eth_* (and debug_*) methods and official migration guidance. To allow this legacy method, add it to enabled_legacy_sei_apis under [evm] in app.toml.", "data": "legacy_sei_deprecated" } } diff --git a/evm/tracing/index.mdx b/evm/tracing/index.mdx index 6bc2972..b6e34ae 100644 --- a/evm/tracing/index.mdx +++ b/evm/tracing/index.mdx @@ -56,8 +56,6 @@ Debug tracing is your primary tool to understand how EVM transactions execute on | `debug_traceBlockByNumber` | Trace an entire block | Block-level analysis | | `debug_traceCall` | Simulate and trace | Testing before execution | | `debug_traceStateAccess` | State access patterns | Performance optimization | - -`debug_traceStateAccess` respects the node's configured `TraceTimeout`. If the trace deadline elapses during replay or serialization, the call aborts with a `context deadline exceeded` error instead of running unbounded. Additionally, the per-module state-access log is capped at 4 MiB of retained key/value payload. When a module exceeds this cap, its trace dump sets `"truncated": true`: the `reads`/`has` fields then reflect only the retained prefix, while the `stats` (operation counts and durations) remain complete and accurate. | `debug_traceTransactionProfile` | Trace plus timing/store-access profiling | Latency breakdown and DB-access analysis | ## Transaction analysis example @@ -438,55 +436,37 @@ These `[evm]` fields in `app.toml` control trace baking: | `trace_bake_enabled` | `false` | Enables the background trace-baking worker that caches results at `/data/trace_db`. RPC nodes only. | | `trace_bake_workers` | `1` | Number of re-execution worker goroutines. | | `trace_bake_queue_size` | `4096` | Bounds the in-flight height queue. When the queue is full, heights are dropped so that consensus never blocks. Dropped blocks fall through to live re-execution. | -| `trace_bake_tracers` | `["callTracer"]` | Which tracers to bake per block. Eligible values: `callTracer`, `prestateTracer`, `flatCallTracer`. | +| `trace_bake_tracers` | `["callTracer"]` | Which tracers to bake per block. Eligible values: `callTracer`, `prestateTracer`, `flatCallTracer`. The node checks the names at startup and fails to start if a name is not a native tracer. | | `trace_bake_window_blocks` | `0` | Rolling prune window: blocks older than `(latest - window)` are pruned. `0` disables pruning, so the cache grows without limit. | | `trace_bake_use_snapshot` | `false` | Uses in-memory memiavl snapshots as the state backend for trace baking when the store backend supports snapshots. | | `trace_bake_snapshot_window` | `64` | Number of recent memiavl snapshots to keep for trace baking. | Trace baking adds a persistent on-disk store at `/data/trace_db` and increases disk usage. The node flushes the store's write-ahead log when it shuts down cleanly. - - ## Tracer allowlist -Caller-supplied `tracer` values on the `debug_trace*` endpoints are gated by `[evm]` config in `app.toml`. By default, only a fixed set of native geth tracers may be requested, and request-supplied JavaScript tracer source is rejected. This deviates from upstream geth, which accepts JavaScript tracers by default. The gate applies to `debug_traceCall`, `debug_traceTransaction`, `debug_traceBlockByNumber`/`debug_traceBlockByHash`, and `debug_traceTransactionProfile`. When no `tracer` is supplied, the default struct logger remains available. +Sei limits which tracers callers can request on `debug_trace*` methods. By default, the node accepts only native geth tracers and rejects JavaScript tracers. Upstream geth accepts JavaScript tracers by default. If a request omits `tracer`, the default struct logger runs. + +Operators set the allowlist in the `[evm]` section of `app.toml`: | Field | Default | Description | | ----- | ------- | ----------- | -| `trace_allowed_tracers` | `["callTracer", "prestateTracer", "flatCallTracer", "4byteTracer", "noopTracer", "muxTracer"]` | Native debug tracer names that callers may request via `TraceConfig.Tracer`. Validated native-only at startup — a non-native or misspelled name fails startup. Set to `[]` to disable all named tracers. Request-supplied JavaScript tracer source can never be enabled through this list. | -| `trace_allow_js_tracers` | `false` | Opts in to request-supplied JavaScript tracer source in `TraceConfig.Tracer`. This executes untrusted code in-process; keep disabled on public/default RPC nodes. Enabling it does **not** widen `trace_allowed_tracers`: native tracer names must still be listed there to be usable. | - -Behavior notes: - -- A tracer name that is not listed in `trace_allowed_tracers` is rejected, unless it is JavaScript source and `trace_allow_js_tracers` is enabled. -- JavaScript tracer source is rejected unless `trace_allow_js_tracers` is set to `true`. -- When `muxTracer` is requested, its nested tracer names are validated recursively against the same allowlist, with a bounded nesting depth of 16. -- `trace_bake_tracers` names are held to the same native-only rule and validated at startup, so a non-native or misspelled baked tracer fails startup instead of being evaluated as JavaScript source on every block. - -The `trace_bake_tracers` note above that eligible values are standard named tracers is subsumed by this native-only startup validation: entries must be native tracer names such as `callTracer`, `prestateTracer`, or `flatCallTracer`. - +| `trace_allowed_tracers` | `["callTracer", "prestateTracer", "flatCallTracer", "4byteTracer", "noopTracer", "muxTracer"]` | Native tracers that callers can request. The node checks the names at startup and fails to start if a name is not a native tracer. Set to `[]` to block all named tracers. | +| `trace_allow_js_tracers` | `false` | Allows JavaScript tracer code in requests. This code runs inside the node, so keep this setting off on public RPC nodes. It does not add tracers to `trace_allowed_tracers`. | +When a request uses `muxTracer`, the node checks the nested tracers against the same allowlist, up to 16 levels deep. ## Struct-logger output cap -The default `debug_trace*` endpoints (`debug_traceCall`, `debug_traceTransaction`, and `debug_traceBlockByNumber`/`debug_traceBlockByHash`) use the built-in struct logger when no custom tracer is supplied. Traces that read many distinct storage slots can retain large amounts of output, so the node bounds the retained struct-logger output per traced transaction. - -The `max_trace_struct_log_bytes` field under `[evm]` in `app.toml` controls this cap: - -| Field | Default | Description | -| ----- | ------- | ----------- | -| `max_trace_struct_log_bytes` | `33554432` (32 MiB) | Bounds the retained struct-logger output, in bytes, per traced transaction on the default `debug_trace*` endpoints. Set to `0` for unlimited (upstream geth behavior). | - -Behavior notes: +The default struct logger can produce large output for transactions that read many storage slots. `max_trace_struct_log_bytes` in the `[evm]` section of `app.toml` limits the output that the node keeps for each traced transaction. The default is 32 MiB (`33554432`). Set it to `0` to remove the limit, as in upstream geth. -- The bound is applied **per transaction**, not per RPC call. Because geth builds a fresh struct logger for each transaction, a `debug_traceBlock*` call over N transactions can retain up to N times this value (and the parallelized block-trace path holds several concurrent traces live). -- A caller-supplied `Limit` larger than `max_trace_struct_log_bytes` is clamped down to the configured value. -- A smaller caller-supplied `Limit` is honored unchanged. -- Custom tracers (for example `callTracer`, `prestateTracer`, or JavaScript tracers) are unaffected, as are requests when the cap is disabled (`0`). +- The limit applies to each transaction, not to each RPC call. A `debug_traceBlock*` call over N transactions can keep up to N times this value. +- If a request sets a larger `Limit`, the node lowers it to `max_trace_struct_log_bytes`. A smaller `Limit` stays as it is. +- Requests that set a tracer, such as `callTracer` or a JavaScript tracer, do not have this limit. ## Removed legacy trace filters -The legacy `*ExcludeTraceFail` endpoints have been removed. For block tracing, use `debug_traceBlockByNumber` or `debug_traceBlockByHash`. For EVM receipts, use `eth_getTransactionReceipt`. There is no block or filter method to discover synthetic logs from Cosmos-originated transactions. If you already know a synthetic transaction hash, enable `sei_getTransactionReceipt` to get its receipt and logs. +The legacy `*ExcludeTraceFail` endpoints have been removed. For block tracing, use `debug_traceBlockByNumber` or `debug_traceBlockByHash`. For EVM receipts, use `eth_getTransactionReceipt`. No RPC method returns receipts or logs for synthetic transactions from Cosmos-originated messages. ## Next steps diff --git a/evm/tracing/javascript-tracers.mdx b/evm/tracing/javascript-tracers.mdx index effbf31..c367375 100644 --- a/evm/tracing/javascript-tracers.mdx +++ b/evm/tracing/javascript-tracers.mdx @@ -5,14 +5,8 @@ keywords: ['javascript tracers', 'custom tracers', 'evm debugging', 'tracer opti --- JavaScript tracers let you create custom debugging logic to analyze EVM transactions on Sei. You can use them for more complex analysis than the built-in tracers support. - - - **JavaScript tracers are disabled by default on Sei.** Unlike upstream geth, Sei does not accept request-supplied JavaScript tracer source on `debug_trace*` endpoints unless a node operator explicitly opts in. To enable them, set `trace_allow_js_tracers = true` under the `[evm]` section of `app.toml`. - - This is a deliberate security hardening: a JavaScript tracer executes caller-supplied code in-process on the node, so it should only be enabled on trusted or private RPC nodes — never on public/default RPC infrastructure. Enabling `trace_allow_js_tracers` does **not** widen the native tracer allowlist (`trace_allowed_tracers`); the two settings are independent. - - If `trace_allow_js_tracers` is `false` (the default), any request supplying JavaScript tracer source to `debug_traceCall`, `debug_traceTransaction`, `debug_traceBlockBy*`, or `debug_traceTransactionProfile` is rejected before any tracer is constructed. If you only need standard analysis, prefer the built-in native tracers (`callTracer`, `prestateTracer`, `flatCallTracer`, `4byteTracer`, `noopTracer`, `muxTracer`), which require no opt-in. +Sei nodes reject JavaScript tracers by default, unlike upstream geth. To use the tracers on this page, you need a node whose operator set `trace_allow_js_tracers = true` in the `[evm]` section of `app.toml`. Because a JavaScript tracer runs code inside the node, operators must enable it only on private RPC nodes. For most analysis, the native tracers work without this setting. See [Tracer allowlist](/evm/tracing#tracer-allowlist). ## Interface overview diff --git a/evm/tracing/troubleshooting.mdx b/evm/tracing/troubleshooting.mdx index 63f4bbf..bcd6fb8 100644 --- a/evm/tracing/troubleshooting.mdx +++ b/evm/tracing/troubleshooting.mdx @@ -79,6 +79,15 @@ This guide covers common issues with EVM transaction tracing on Sei and gives pr ### 3. JavaScript tracer errors +**Error**: `debug tracer "..." is not allowed; JavaScript tracers are disabled and only native tracers listed in evm.trace_allowed_tracers may be used` + +**Cause**: The node accepts only the native tracers in `trace_allowed_tracers` and rejects JavaScript tracers by default. For `muxTracer`, a nested tracer that is not on the list fails with `nested debug tracer "..." is not allowed`. + +**Solutions**: + +- Use a tracer from the default list: `callTracer`, `prestateTracer`, `flatCallTracer`, `4byteTracer`, `noopTracer`, or `muxTracer`. +- If you run the node, add the native tracer to `trace_allowed_tracers`, or set `trace_allow_js_tracers = true` to allow JavaScript tracers. Keep JavaScript tracers off on public RPC nodes. See [Tracer allowlist](/evm/tracing#tracer-allowlist). + **Error**: `SyntaxError` or `ReferenceError` in a JavaScript tracer **Common issues**: @@ -142,35 +151,6 @@ This guide covers common issues with EVM transaction tracing on Sei and gives pr ### 4. Network and connection issues - - -#### Tracer not allowed / JavaScript tracers disabled - -**Error**: `debug tracer "..." is not allowed; JavaScript tracers are disabled and only native tracers listed in evm.trace_allowed_tracers may be used` - -**Cause**: Sei gates which tracers callers may request through `TraceConfig.Tracer` on the `debug_traceCall`, `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceTransactionProfile` endpoints. By default only the native tracers listed in the `[evm]` `trace_allowed_tracers` config are accepted, and request-supplied JavaScript tracer source is rejected. This deviates from upstream geth, which accepts JavaScript tracers by default. - -**Solutions**: - -- Use one of the allowlisted native tracers. The shipped default allowlist is: - - ```toml - [evm] - trace_allowed_tracers = ["callTracer", "prestateTracer", "flatCallTracer", "4byteTracer", "noopTracer", "muxTracer"] - ``` - -- If you are an operator and need to permit a native tracer that is not listed, add its name to `trace_allowed_tracers`. Only native geth tracer names are accepted here; a typo or JavaScript source causes the node to fail at startup. Setting the list to `[]` disables all named tracers. -- To allow request-supplied JavaScript tracer source, an operator must explicitly opt in: - - ```toml - [evm] - trace_allow_js_tracers = true - ``` - - This executes untrusted code in-process and should be kept disabled on public/default RPC nodes. Enabling it does **not** widen `trace_allowed_tracers`: native tracer names must still be listed there to be usable. - -When using `muxTracer`, the nested tracer names in `TracerConfig` are validated recursively against the same allowlist, with a bounded nesting depth of 16. A nested name that is not allowlisted produces a `nested debug tracer "..." is not allowed` error. Also note that `trace_bake_tracers` names are validated as native-only at startup, so a non-native or mistyped name there will fail node startup rather than being evaluated as JavaScript. - **Error**: `connection refused` or `network timeout` **Solutions**: diff --git a/evm/transactions.mdx b/evm/transactions.mdx index e14f685..203c18c 100644 --- a/evm/transactions.mdx +++ b/evm/transactions.mdx @@ -290,25 +290,6 @@ Before Sei accepts an EVM transaction, it performs strict semantic validation of - [Transaction types](/evm/evm-parity/transaction-types#access-list-and-auth-list-entry-validation) is the canonical field-level reference. It covers signature-value byte caps and zero-padding rules, access-list entries, and EIP-7702 authorization lists. - [Differences with Ethereum](/evm/differences-with-ethereum#evm-transaction-envelope-restrictions) covers envelope-level restrictions: no Cosmos wrapper fields, canonical protobuf encoding, and whole-block rejection on decode failure. - - -### Cosmos transaction public-key validation - -For Cosmos transactions, the ante handlers now validate every public key provided in a transaction before deriving addresses from it or persisting it to account state. This validation runs during both `CheckTx` and `DeliverTx`, so a transaction carrying a malformed key is rejected rather than silently storing an unusable key on the account. - -A public key is rejected when: - -- A `secp256k1` key has the wrong byte size or fails to parse as a valid curve point — returns `ErrInvalidPubKey`. -- A multisig key has a threshold of zero, or a threshold greater than the number of member keys — returns `ErrInvalidPubKey`. -- A multisig key is nested more than 5 levels deep — returns `ErrInvalidPubKey`. -- The number of member keys consumed exceeds the per-pubkey signature budget derived from the auth module's `TxSigLimit` — returns `ErrTooManySignatures`. - -The recursive work budget is kept local to each provided public key, so one multisig tree is bounded on its own without changing the aggregate `TxSigLimit` behavior applied to keys persisted to account state. - - - If you build and sign Cosmos transactions manually (for example, assembling multisig keys), ensure member keys are well formed and that multisig nesting stays within 5 levels to avoid rejection. - - ### Receipts for nonce-bumping failed transactions Some EVM transactions pass basic validation and bump the sender's nonce, but still fail during state transition. One example is a transaction whose gas limit clears the intrinsic-gas check but falls short of the EIP-7623 floor-data-gas requirement. This can occur in normal operation after Pectra. Because these transactions bump the nonce, they are considered to have happened and therefore produce a receipt. diff --git a/learn/sei-giga-developers.mdx b/learn/sei-giga-developers.mdx index 20681ab..6987707 100644 --- a/learn/sei-giga-developers.mdx +++ b/learn/sei-giga-developers.mdx @@ -38,7 +38,7 @@ Transaction ingress will initially route complete transactions to validator lane | Finality | Block inclusion / finalized tag | Ordering finality (measured <250 ms), execution receipt, then state attestation finality | Wait for the stage your flow requires (see [below](#finality-on-giga-which-signal-to-wait-for)) | | Mempool | Public pending pool, gossip, frontrunning games | No traditional public mempool. Complete transactions route to lanes before Sedna. The later [Sedna](/learn/sei-giga-specs#sedna-private-dissemination) milestone adds coded symbol bundles | Drop pending-pool assumptions. There will be no pending stream to watch | | Priority fee | Paid to the block proposer, and buys inclusion favor | Strictly enforced for ordering, then socialised across validators by stake × liveness | Use the tip to express urgency. The protocol does not pay a specific proposer for preferential treatment | -| State proofs | `eth_getProof` against the block state root | No state root. [BUD proofs](/learn/sei-giga-specs#block-update-digests-buds) against attested per-block digests | Avoid a hard dependency on `eth_getProof`. Plan for BUD-based verification | +| State proofs | `eth_getProof` against the block state root on Ethereum. Sei does not support `eth_getProof` | No state root. [BUD proofs](/learn/sei-giga-specs#block-update-digests-buds) against attested per-block digests | Avoid a hard dependency on `eth_getProof`. Plan for BUD-based verification | | Randomness | `PREVRANDAO` (beacon randomness) | `PREVRANDAO` not supported with Ethereum semantics | Use a VRF or oracle for randomness | | Blob transactions | EIP-4844 type-3 transactions | Will not be supported (Giga will remain an L1, not a rollup DA consumer) | Nothing to do unless you post blobs | | Block gas limit | Fixed per-block constant | No Ethereum-style fixed block gas limit. Lane and consensus parameters will bound throughput | Do not encode assumptions about a specific block gas limit | @@ -168,7 +168,7 @@ The retired native Oracle precompile at `0x0000000000000000000000000000000000001 - Build EVM-only. [SIP-3](/learn/sip-03-migration) consolidates Sei to an EVM-only stack ahead of Giga: CosmWasm no longer accepts new deployments. IBC is disabled in both directions: Proposals [116](https://seistream.app/proposals/116) and [120](https://seistream.app/proposals/120) disabled inbound transfers, and [121](https://seistream.app/proposals/121) disabled outbound transfers on July 31, 2026. Existing IBC balances can still move within Sei but can no longer be redeemed over IBC on their origin chain. For external data, use the providers documented for Sei: [API3](/evm/oracles/api3), [Chainlink](/evm/oracles/chainlink), [Pyth](/evm/oracles/pyth-network), and [RedStone](/evm/oracles/redstone). - Audit for hot slots. Per-user state isolation is the single most effective change for parallel throughput. The first Ares phase went live with the mandatory Sei Mainnet v6.6 upgrade on August 4, 2026. Broader Ares work continues in later releases. - Classify your confirmation flows. Decide which flows only need the fixed order, which act on an execution receipt, and which wait for state attestation. The three-stage model will then need only a configuration change, not a redesign. -- Remove fragile dependencies. Anything that relies on `eth_getProof` and state roots ([current behavior](/evm/evm-parity/state-proofs)), `PREVRANDAO` randomness, blob transactions, or watching the public mempool will need a plan. +- Remove fragile dependencies. Anything that relies on `eth_getProof` and state roots ([not supported on Sei](/evm/evm-parity/evm-compatibility#proof-verification)), `PREVRANDAO` randomness, blob transactions, or watching the public mempool will need a plan. - Do not wait for a "Giga chain." There will not be one. Giga will arrive on the network where you are already deployed. Standard EVM contracts that do not depend on retired Sei-native surfaces are designed to carry over without redeployment. ## Related diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index 431c22b..7281f02 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -505,13 +505,9 @@ scrape_configs: - targets: ['localhost:9100'] ``` - - ## Seed node Prometheus metrics -Seed nodes now expose a Prometheus `/metrics` endpoint, so they can be scraped like any other node. Because a seed serves no RPC, this endpoint is the only observability surface it has — peer reachability and peer headroom both come from its p2p metrics (for example `tendermint_p2p_peers`). - -To enable it, set the usual instrumentation fields in `config.toml` under `[instrumentation]`: +Since v6.7.0, seed nodes serve Prometheus metrics like other nodes. A seed serves no RPC, so its p2p metrics, such as `tendermint_p2p_peers`, are the only way to watch it. Enable them in the `[instrumentation]` section of `config.toml`: ```toml [instrumentation] @@ -519,62 +515,36 @@ prometheus = true prometheus-listen-addr = ":26660" ``` -Every exposed series is stamped with the node's `chain_id` label, matching the behavior of full nodes. - -For a seed node, a failure to bind the Prometheus listener (for example, the port is already in use or the address is unparseable) is **fatal**: the seed fails to start. This is deliberate — a seed's value is only realized when it is observable, so an unambiguous crash is preferable to a seed that discovers peers while blind. - -A full node behaves differently: a bind failure is **non-fatal**. The full node logs the error and continues running, because it still serves RPC as an in-band way to observe its health. +If a seed node cannot open the Prometheus listener, the seed fails to start. For example, the port can be in use, or the address can be invalid. A full node logs the error and continues to run. -The `tendermint_p2p_peers` gauge is present from node startup rather than appearing only after the first refresh interval, so an alert comparing peer count against the connection cap matches immediately rather than sitting unmatched during the startup window. - -### Metrics server timeouts - -The Prometheus metrics HTTP server enforces request timeouts to bound how long a slow reader can hold a request slot: - -| Timeout | Value | -| --- | --- | -| `ReadHeaderTimeout` | 10s | -| `ReadTimeout` | 10s | -| `WriteTimeout` | 30s | -| `IdleTimeout` | 60s | - -### `max-open-connections` behavior +### Metrics server limits -The `max-open-connections` field under `[instrumentation]` is not a socket limit despite the name. It becomes promhttp's `MaxRequestsInFlight`: the maximum number of scrapes served concurrently. Requests past the limit receive an HTTP `503` rather than being queued, and a slow reader occupies a slot until it finishes or hits the `WriteTimeout`. A value of `0` means unlimited. +The metrics server uses a 10s read header timeout, a 10s read timeout, a 30s write timeout, and a 60s idle timeout. -Keep this value above the number of scrapers that can overlap — for example, an HA Prometheus pair, blackbox probes, and a human running `curl` — so a burst of concurrent scrapes does not cause a healthy node to report as unscrapeable. +`max-open-connections` under `[instrumentation]` is not a connection limit. It caps the number of scrapes that the server handles at the same time. Requests over the cap get HTTP `503`. The server does not queue them. A slow scraper holds its slot until it finishes or reaches the write timeout. `0` means no limit. Set the value higher than the number of scrapers that can overlap, such as an HA Prometheus pair, probes, and manual `curl` requests. +## `chain_id` label on metrics +Since v6.7.0, the node adds a `chain_id` label to every metric that it exports. This includes the Tendermint Prometheus metrics, the Cosmos SDK telemetry, and the OpenTelemetry metrics (`evmrpc_*`, `flatkv_*`, `litt_*`, and `pebble_*`). If you already set `chain_id` in `global-labels` under `[telemetry]`, the node keeps your value. +Update PromQL that aggregates across series, such as `sum(...) without (...)` or `by (...)`, to account for the new label. To scope a query to one chain, add a `chain_id` match. -## Cosmos SDK / Tendermint telemetry chain_id label +## Tendermint internal metrics -Node telemetry automatically injects a `chain_id` global label into the Cosmos SDK / Tendermint Prometheus telemetry (the `GlobalLabels` of the `[telemetry]` configuration) based on the node's client chain ID. The label is appended only when a `chain_id` label is not already configured in `GlobalLabels`, so an explicit operator-set value always takes precedence. This is distinct from the `chain_id` constant label applied to the OpenTelemetry `sei_chain` namespace described above — it covers the separate Cosmos SDK/Tendermint telemetry path. - -Because every emitted series carries this label, update any PromQL that aggregates across series (for example `sum(...) without (...)` or `by (...)` clauses) to account for `chain_id`, and add a `chain_id` match where you want to scope a query to a single chain — useful when one Prometheus instance scrapes nodes on more than one chain. - -### New Tendermint internal metrics subsystems - -Additional Prometheus metrics are exported under the `tendermint` namespace in these new subsystems: +The `tendermint` namespace also has these subsystems: | Subsystem prefix | Description | | --- | --- | -| `tendermint_internal_autobahn_avail_*` | Autobahn availability metrics, including the commit and app road index, the commit and app global block number, proposal-to-commit latency, and commit-to-commit latency (the latter labeled by `timeouts`). | -| `tendermint_internal_autobahn_data_*` | Autobahn data latency metrics (a `latency` histogram labeled by `resource` and `stage`) tracking resource processing from production to the given stage. | -| `tendermint_internal_p2p_mux_*` | p2p mux stream metrics, including stream `latency`, `in_flight` open streams, and per-stream `send_msgs`, `recv_msgs`, `send_bytes`, and `recv_bytes` counters (labeled by `role` and `kind`). | +| `tendermint_internal_autobahn_avail_*` | Autobahn availability metrics: the commit and app road index, the commit and app global block number, the proposal-to-commit latency, and the commit-to-commit latency (with a `timeouts` label). | +| `tendermint_internal_autobahn_data_*` | A `latency` histogram for Autobahn data, with `resource` and `stage` labels. It measures the time from the production of a resource to each stage. | +| `tendermint_internal_p2p_mux_*` | p2p mux stream metrics: the stream `latency`, the number of open streams (`in_flight`), and the per-stream counters `send_msgs`, `recv_msgs`, `send_bytes`, and `recv_bytes`. These metrics have `role` and `kind` labels. | ## EVM RPC OpenTelemetry metrics The EVM RPC layer emits OpenTelemetry metrics through the process-wide `MeterProvider` (for example, a Prometheus exporter). It emits these metrics in parallel with the legacy `sei_*` metrics, so you can migrate dashboards incrementally. - - - -Every OpenTelemetry metric series exported through the Prometheus exporter (the `sei_chain` namespace, including the `evmrpc_*`, `flatkv_*`, `litt_*`, and `pebble_*` metrics documented below) now carries a `chain_id` constant label derived from the node's chain ID. The label is applied to every emitted series, so dashboards and alerting queries can group or filter by chain — for example when a single Prometheus instance scrapes nodes on more than one chain. Update any PromQL that aggregates across series (for example `sum(...) without (...)` or `by (...)` clauses) to account for the new label, and add a `chain_id` match where you want to scope a query to a single chain. - - ### Available EVM RPC metrics | Metric | Type | Description | @@ -583,7 +553,7 @@ Every OpenTelemetry metric series exported through the Prometheus exporter (the | `evmrpc_websocket_connects_total` | Counter | Number of new WebSocket connections. | | `evmrpc_redirected_requests_total` | Counter | Number of EVM RPC requests forwarded to another validator. Labeled by `endpoint` and `connection`. | | `evmrpc_historical_debug_trace_attempts_total` | Counter | Number of `debug_trace*` requests that target historical blocks beyond the configured max block lookback. Labeled by `endpoint` and `connection`. | -| `evmrpc_requests_rejected_total` | Counter | Number of JSON-RPC requests rejected by admission control. Labeled by `protocol` (`http` or `ws`) and `reason` (`oversize` or `busy`). On HTTP (:8545), an `oversize` request body exceeds the configured `max_request_body_bytes` cap and is rejected with HTTP 413, while `busy` means the `max_concurrent_request_bytes` budget is exhausted and the request is rejected with HTTP 429. On WebSocket (:8546), `oversize` frames exceed `max_request_body_bytes` and close the connection with WebSocket close code 1009 (no JSON-RPC error response), while `busy` means the connection waited for concurrent-byte budget longer than `ws_admission_timeout` and the peer receives JSON-RPC error `-32005` before the connection is closed (#3818). | +| `evmrpc_requests_rejected_total` | Counter | Number of JSON-RPC requests that admission control rejected. Labeled by `protocol` (`http` or `ws`) and `reason`. `oversize` means the request was larger than `max_request_body_bytes` (HTTP 413, or WebSocket close code `1009`). `busy` means the `max_concurrent_request_bytes` budget was full (HTTP 429, or JSON-RPC error `-32005` after `ws_admission_timeout` on WebSocket). See [Frame size and concurrency limits](/evm/evm-parity/websocket#frame-size-and-concurrency-limits). | The `evmrpc_request_latency_seconds` histogram carries these labels: @@ -641,97 +611,69 @@ FlatKV metrics carry these labels where applicable: | Label | Description | | --- | --- | -| `db` | The data DB that the measurement applies to (for example, `account`, `storage`, `code`, or `legacy`). Present on per-DB metrics such as `flatkv_commit_batch_latency`, `flatkv_flush_latency`, `flatkv_num_kv_pairs`, `flatkv_pending_writes`, `flatkv_import_kv_pairs`, and `flatkv_import_worker_flush_latency`. | +| `db` | The data DB that the measurement applies to (`account`, `code`, `storage`, or `misc`). Present on per-DB metrics such as `flatkv_commit_batch_latency`, `flatkv_flush_latency`, `flatkv_num_kv_pairs`, `flatkv_pending_writes`, `flatkv_import_kv_pairs`, and `flatkv_import_worker_flush_latency`. | | `success` | Boolean that indicates whether the operation succeeded. Present on latency and attempt metrics that can fail. | | `read_only` | Boolean, present on `flatkv_open_latency`, that indicates whether the store was opened read-only. | -### Enabling Pebble internal metrics - -A single FlatKV-level knob controls Pebble's internal (per-DB) metrics: `enable-pebble-metrics` under `[state-commit.flatkv]` in `app.toml` (default `true`). The node honors this key when it is present, but the `app.toml` that `seid init` generates does not include it. To change the default, add the key manually. The value propagates to every data DB (account, code, storage, legacy, and metadata) during initialization. It overrides any per-DB `EnableMetrics` settings. Configure Pebble metrics through this knob, not through the individual per-DB settings. +### Pebble internal metrics +FlatKV turns on Pebble's internal metrics for each data DB by default. The node does not apply an `enable-pebble-metrics` key from `app.toml`. ## Pebble estimated read/write metrics -SeiDB can emit lightweight, opt-in estimates of logical read and write operations observed by its Pebble-backed wrappers. These counters are disabled by default and are intended to approximate read amplification (estimated reads divided by estimated writes) without the overhead of Pebble's full internal metrics. - -### Available metrics - -| Metric | Type | Unit | Description | -| --- | --- | --- | --- | -| `pebble_estimated_reads` | Counter | `{count}` | Estimated logical PebbleDB reads observed by SeiDB wrappers. | -| `pebble_estimated_writes` | Counter | `{count}` | Estimated logical PebbleDB writes observed by SeiDB wrappers. | - -Both counters carry a `db` attribute identifying the database the measurement applies to (derived from the base name of the data directory), so you can distinguish FlatKV data DBs, the state-store backend, and receipt storage. +SeiDB can count estimated logical reads and writes on its PebbleDB databases. The counters are off by default. They cost less than Pebble's full internal metrics, and you can use them to estimate read amplification (reads divided by writes). -### Enabling the counters +| Metric | Type | Description | +| --- | --- | --- | +| `pebble_estimated_reads` | Counter | Estimated logical PebbleDB reads. | +| `pebble_estimated_writes` | Counter | Estimated logical PebbleDB writes. | -These estimates are controlled per subsystem in `app.toml`. All default to `false`: +Both counters have a `db` attribute. Its value is the name of the data directory, so you can tell the FlatKV data DBs, the state store, and the receipt store apart. Turn the counters on per store in `app.toml`: -| Config key | Section | Description | +| Config key | Section | Store | | --- | --- | --- | -| `enable-read-write-metrics` | `[state-commit.flatkv]` | Emits estimated read/write counters for FlatKV's Pebble DBs. | -| `ss-enable-read-write-metrics` | `[state-store]` | Emits estimated PebbleDB MVCC read/write counters for the state-store backend. Applies when `ss-backend = "pebbledb"`. | -| `enable-read-write-metrics` | `[receipt-store]` | Emits estimated read/write counters for Pebble-backed receipt storage. | - -For example, to enable the FlatKV counters, add the following to `app.toml`: +| `enable-read-write-metrics` | `[state-commit.flatkv]` | FlatKV data DBs | +| `ss-enable-read-write-metrics` | `[state-store]` | State store, when `ss-backend = "pebbledb"` | +| `enable-read-write-metrics` | `[receipt-store]` | Pebble-based receipt store | ```toml [state-commit.flatkv] enable-read-write-metrics = true ``` +## Log query parallelism for `littidx` - -## Tuning littidx eth_getLogs parallelism - -When the receipt store uses the `littidx` backend, each `eth_getLogs` query scans the requested block range across a bounded worker pool instead of one block at a time. Per-block tag index scans and litt body reads are independent, so fanning them across multiple workers reduces latency on wide-range queries while results stay in strict `(block, txIndex)` order. - -The `log-filter-parallelism` config key under `[receipt-store]` in `app.toml` bounds how many blocks a single query scans concurrently. It defaults to `16` and applies only to the `littidx` backend. A value `<= 0` falls back to the default. +With the `littidx` receipt store, each `eth_getLogs` query scans several blocks at the same time. Results stay in block and transaction order. `log-filter-parallelism` under `[receipt-store]` sets how many blocks one query scans at once. The default is `16`, and a value of `0` or less uses the default. The setting has no effect on `pebbledb`. ```toml [receipt-store] log-filter-parallelism = 16 ``` - - ## Per-block hash logger -The state-commit store can record a per-block CSV of named block hashes to disk as a debugging and forensics tool. For each committed block it logs the memIAVL per-module and root hashes, the flatKV per-DB and root hashes, the application hash, the Tendermint block hash, the result hash (the merkle root over the block's deterministic transaction results, equal to the next block's `LastResultsHash`), and the changeset hash. Recording the same hashes under one block number on every node makes it straightforward to pinpoint where and when block-hash computation diverges across nodes. - -The feature is **enabled by default**. By default it writes files into a `hash.log` directory under the state-commit store's data directory (that is, `/data/hash.log`). - -### Configuration +The state-commit store writes a CSV file of named hashes for each block. Each row has the memIAVL module and root hashes and the FlatKV DB and root hashes. It also has the app hash, the block hash, the results hash (the next block's `LastResultsHash`), and the changeset hash. To find where two nodes diverged, compare their logs with [`seidb hashlog compare`](/node/technical-reference#inspecting-hash-log-archives). -The hash logger is configured with five `app.toml` fields under `[state-commit]`: +The hash logger is on by default and writes to `/data/hash.log`. With the default settings, it keeps up to 16 GiB of closed log files. Configure it in the `[state-commit]` section of `app.toml`: | Config key | Default | Description | | --- | --- | --- | -| `sc-hash-logger-enable` | `true` | Turns per-block hash logging on or off. | -| `sc-hash-logger-directory` | empty | Directory for hash log files. When empty, defaults to `/data/hash.log`. | -| `sc-hash-logger-blocks-to-retain` | `0` | Number of most-recent blocks to keep on disk. `0` disables block-count retention (the disk-size cap is then the only bound). | -| `sc-hash-logger-target-file-size` | `16 MB` | Size in bytes a log file may reach before it is sealed and a new one is opened. Must be greater than `0`. | -| `sc-hash-logger-max-disk-size` | `16 GB` | Backstop cap in bytes on the total size of sealed log files. `0` disables the disk-size cap (block-count retention is then the only bound). | +| `sc-hash-logger-enable` | `true` | Turns the hash logger on or off. | +| `sc-hash-logger-directory` | empty | Directory for the log files. Empty means `/data/hash.log`. | +| `sc-hash-logger-blocks-to-retain` | `0` | Number of recent blocks to keep. `0` turns off the block limit. | +| `sc-hash-logger-target-file-size` | `16777216` (16 MiB) | Size in bytes at which the logger closes a file and starts a new one. Must be greater than `0`. | +| `sc-hash-logger-max-disk-size` | `17179869184` (16 GiB) | Maximum total size in bytes of closed log files. `0` turns off the size limit. | -Retention is disk-driven by default: up to roughly 16 GiB of sealed files are kept, with block-count retention disabled. If both `sc-hash-logger-blocks-to-retain` and `sc-hash-logger-max-disk-size` are set to `0`, no retention bound applies and the logs grow without limit — a deliberate operator choice. - -For example, to disable the hash logger, add the following to `app.toml`: +If you set both `sc-hash-logger-blocks-to-retain` and `sc-hash-logger-max-disk-size` to `0`, the logs grow without limit. To turn the hash logger off: ```toml [state-commit] sc-hash-logger-enable = false ``` - - ## LittDB OpenTelemetry metrics -LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` instead of a private Prometheus client. When `MetricsEnabled` is set, LittDB records its metrics into the global provider. How those metrics are exported depends on `MetricsServeEndpoint` (default `false`): - -- When `MetricsServeEndpoint` is `false` (the default), LittDB records into the already-configured global `MeterProvider` and leaves exporting to the embedding application. The application is responsible for standing up the Prometheus exporter and serving the registry. No exporter or `/metrics` server is created by LittDB, and `MetricsPort` is ignored. -- When `MetricsServeEndpoint` is `true`, LittDB stands up its own Prometheus exporter on the global provider and serves `/metrics` on `MetricsPort` (default `9101`). - -`MetricsPort` is ignored unless both `MetricsEnabled` and `MetricsServeEndpoint` are `true`. The `MetricsNamespace` and `MetricsRegistry` config fields were removed, and all metric names use a fixed `litt_` prefix. +LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` instead of a private Prometheus client. If `MetricsEnabled` is `true`, LittDB records its metrics in that provider. By default (`MetricsServeEndpoint = false`), the application that uses LittDB exports the metrics, and LittDB does not start a `/metrics` server. If `MetricsServeEndpoint` is `true`, LittDB serves `/metrics` on `MetricsPort` (default `9101`). LittDB no longer has the `MetricsNamespace` and `MetricsRegistry` config fields, and all metric names use a fixed `litt_` prefix. ### Available LittDB metrics @@ -739,7 +681,7 @@ LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` | --- | --- | --- | --- | | `litt_table_size_bytes` | Gauge | bytes | The size of individual tables in the database. | | `litt_table_key_count` | Gauge | count | The number of keys in individual tables in the database. | -| `litt_open_iterator_count` | Gauge | count | The number of currently open iterators for individual tables in the database. A persistently nonzero value indicates a leaked iterator. Garbage collection proceeds while iterators are open — an iterator pins its snapshot segments via reservations so their files survive until it closes — so a leaked iterator pins those segment files on disk indefinitely rather than suspending garbage collection. | +| `litt_open_iterator_count` | Gauge | count | The number of currently open iterators for individual tables in the database. A persistently nonzero value indicates a leaked iterator. Garbage collection still runs, but a leaked iterator keeps the segment files it reads on disk until it closes. | | `litt_bytes_read` | Counter | bytes | The number of bytes read from disk since startup. | | `litt_keys_read` | Counter | count | The number of keys read from disk since startup. | | `litt_cache_hits` | Counter | count | The number of cache hits since startup. | @@ -759,11 +701,10 @@ LittDB emits its metrics through the process-wide OpenTelemetry `MeterProvider` | `litt_chunk_cache_keys_added` | Counter | count | The number of keys added to the chunk cache. | | `litt_chunk_cache_weight_added_bytes` | Counter | bytes | The weight of the entries added to the chunk cache. | | `litt_chunk_cache_eviction_latency_seconds` | Histogram | seconds | Eviction latency of the chunk cache. | - -| `litt_compression_latency_seconds` | Histogram | seconds | Latency of compressing a batch of values before they are written. Emitted only for tables with value compression enabled. | -| `litt_compression_uncompressed_bytes` | Counter | bytes | The number of uncompressed value bytes submitted to compression since startup. | -| `litt_compression_compressed_bytes` | Counter | bytes | The number of compressed value bytes produced by compression since startup. Compared against `litt_compression_uncompressed_bytes`, this gives the aggregate compression ratio and total bytes saved. | -| `litt_compression_ratio` | Histogram | ratio | The per-batch compression ratio (compressed bytes divided by uncompressed bytes); lower is better. | +| `litt_compression_latency_seconds` | Histogram | seconds | Time to compress a batch of values before LittDB writes them. Emitted only for tables with compression enabled. | +| `litt_compression_uncompressed_bytes` | Counter | bytes | Value bytes sent to compression since startup. | +| `litt_compression_compressed_bytes` | Counter | bytes | Compressed bytes produced since startup. Divide by `litt_compression_uncompressed_bytes` to get the overall compression ratio. | +| `litt_compression_ratio` | Histogram | ratio | Compressed bytes divided by uncompressed bytes, per batch. Lower is better. | ### Attributes diff --git a/node/giga-storage-migration.mdx b/node/giga-storage-migration.mdx index 5a9099f..75cfce4 100644 --- a/node/giga-storage-migration.mdx +++ b/node/giga-storage-migration.mdx @@ -197,146 +197,77 @@ the node and delete the configured EVM SS directory. Everything above concerns the **SS** (State Store) layer. The **SC** (State Commit) layer has a separate migration path. It moves the hot `evm/` data out -of `memiavl` and into FlatKV in place, without a state sync. The `sc-write-mode` -setting in `app.toml` drives this path entirely. You coordinate it across a -quorum by stopping the nodes, editing the config, and restarting them. +of `memiavl` and into FlatKV in place, without a state sync. -Unlike the SS split, the SC-side migration **does change how `evm/` data -contributes to the app hash**. The contribution moves from the memiavl IAVL root -to the FlatKV lattice hash. Because of this, every validator in a quorum must -flip at the same coordinated stop. If a node flips while its peers are still on -the old mode, it produces a different AppHash on the next block, and consensus -halts. The safe sequence is always: stop every node, rewrite `app.toml` -everywhere, and restart every node. +Unlike the SS split, the SC migration **changes how `evm/` data contributes to +the app hash**. The contribution moves from the memiavl IAVL root to the FlatKV +lattice hash. Every validator must therefore switch at the same block, or +consensus halts. -Do not run this SC-side flow against Sei Testnet or Sei Mainnet nodes unless your version's release notes explicitly list it as supported. The cluster and devnet integration harness tests this FlatKV EVM migration flow. +### How the migration starts -### Write modes +Since v6.7.0, governance controls the migration, and nodes follow it without +config changes: -The migration is a transition from the `memiavl_only` write mode to -`migrate_evm`. In `memiavl_only` (v0), memiavl is the sole SC backend and FlatKV -is not allocated. The in-flight mode, `migrate_evm`, drains `evm/` keys from -memiavl into FlatKV. After the migration completes, flip `sc-write-mode` -again to `evm_migrated`, so that later restarts do not start the migration -manager. - -`evm_migrated` is not the last mode. Three more modes sit between it and the -terminal mode: - -- `migrate_all_but_bank` drains every remaining module except `bank/` from - memiavl into FlatKV. -- `all_migrated_but_bank` is the steady state after that drain completes. -- `migrate_bank` drains the final `bank/` module. - -`flatkv_only` is the fully supported **terminal** steady-state write mode. -FlatKV is the sole SC backend, and `memiavl` is not allocated at all. FlatKV -serves the SC state of every module. State-sync snapshot export and restore -work correctly in this mode, and so does app-hash parity. A node can therefore -boot or state sync directly into the post-migration shape, without ever running -the migration manager. Such a node uses `flatkv_only`. - -`flatkv_only` is valid only for a node whose modules have all been drained out -of memiavl. It is not a flip target for an `evm_migrated` node, which still -holds `bank/` and every other non-EVM module in memiavl. It becomes the valid -flip target after `migrate_bank` reports complete on the node (migration -version 3, all modules in FlatKV). Then restart with -`sc-write-mode = "flatkv_only"` to reach the terminal steady state. - - -For backward compatibility with v6.4/v6.5 `app.toml` files, the -`sc-write-mode` config field (and the `--sc-write-mode` flag) still accepts the -legacy value `"cosmos_only"`. It is parsed as equivalent to `memiavl_only`, so -an older config that pins `sc-write-mode = "cosmos_only"` resolves to the same -memIAVL-only routing and continues to work after upgrading, with no edit -required before the node starts. - -A correctness bug in the WAL replay path is fixed. On replay (catchup, -read-only clone, snapshot export, and state-sync restore), the bug dropped empty -(zero-length) values written with no delete flag. This made the FlatKV state and -the consensus AppHash diverge from the live chain. Empty-value writes are now -preserved across a WAL round-trip and a state sync, which makes `flatkv_only` -state sync reliable. - -While a node is in a migration mode: - -- Caller reads of not-yet-migrated keys fall back to FlatKV for new keys written - after the migration started, and to memiavl otherwise. -- A merging iterator over both backends serves iteration. It queries memiavl - first, and FlatKV wins on ties, so range scans see the complete key set during - a migration. -- The migration boundary advances at most once per block. - - - -### The `auto` write mode (runtime transitions without restarts) - -`auto` is an additional `sc-write-mode` value that removes the stop-edit-restart -step between each migration stage. Instead of fixing the write mode in `app.toml`, -`auto` derives the **effective** write mode from the migration metadata persisted -in FlatKV. A node stays on `auto` in config across the entire migration chain, -and the effective mode advances through `memiavl_only` → `migrate_evm` → -`evm_migrated` → `migrate_all_but_bank` → `all_migrated_but_bank` → -`migrate_bank` → `flatkv_only` via coordinated runtime transitions, with no -restart required at any stage. +- By default, every node runs in the `auto` write mode. The node ignores + `sc-write-mode` and derives its mode from the migration state on disk. +- The `NumKeysToMigratePerBlock` parameter in the `migration` params subspace + sets how many keys move per block. The default, `0`, keeps the migration + paused. A `ParameterChangeProposal` can raise it to at most 1,000,000. +- When the parameter becomes greater than `0`, every `auto` node switches from + `memiavl_only` to `migrate_evm` at the same height and starts to move keys. + The node does not restart. -```toml copy -[state-commit] -sc-write-mode = "auto" -``` +In v6.7, governance can start only the EVM migration. The later write modes +(`migrate_all_but_bank`, `all_migrated_but_bank`, `migrate_bank`, and +`flatkv_only`) exist, but no parameter starts them. -Under `auto`: - -- FlatKV is **lazy**. On a fresh node the FlatKV directory is not created on disk - until the first `migrate_evm` transition occurs. While the directory is absent, - the store is effectively `memiavl_only` and FlatKV does not contribute to the - app hash at all. -- Transitions walk the migration chain **one step forward at a time**, and a - migration stage must have completed its drain before the node can advance to - the following steady state. Skipping steps, moving backward, or exiting a - migration mid-flight is rejected. -- Transitions are **deterministic and level-triggered** across the quorum: every - node performs the same transition at the same height. Because migration writes - feed the app hash, a transition that is not yet persisted is re-evaluated on - every commit, so a node that restarts inside the pre-commit window re-fires the - transition and self-heals rather than diverging. -- On restart, the effective mode is re-derived from the on-disk migration state. - An in-flight migration resumes in its migration mode; a completed one comes up - in the following steady state even if no explicit completion flip was issued. - -State proofs remain supported only for memiavl-resident data — FlatKV has no -proof builder. This is not specific to `auto`, but it becomes operator-visible -once an `auto` chain migrates stores into FlatKV. - -**Never set `sc-write-mode = "auto"` on an existing `flatkv_only` -node.** `auto` is only valid for a node whose history began in `memiavl` (for -example, a node switching from `memiavl_only`). A node that started in FlatKV -mode must keep `flatkv_only` forever. Depending on the node's on-disk metadata, -setting `auto` on a flatkv-origin node fails in one of two ways: with migration -metadata present (for example a state-synced node), every commit fails with a -version-mismatch error; with metadata absent (a genesis FlatKV chain), mode -derivation resolves `memiavl_only` and reads silently route to an empty -memiavl, serving incorrect empty results. - -### Operator-facing knobs - -**`sc-keys-to-migrate-per-block`** (`app.toml`, `[state-commit]` section) -controls how many EVM keys the in-flight migration drains from memiavl into -FlatKV per block. The default is `1024`, which is appropriate for production -drains. A lower value spreads the migration across more blocks. The value must -be `> 0`. The node ignores it entirely when `sc-write-mode` is not a migration -mode. +During the migration, the node reads and iterates over both backends. Each key +is in one backend at a time, because the migration deletes a key from memiavl +when it copies the key to FlatKV. The migration boundary advances at most once +per block. FlatKV has no proof builder, so state proofs work only for data that +is still in memiavl. + +### Pinning a write mode + +To pin a write mode, set both keys in the `[state-commit]` section of +`app.toml`. The generated `app.toml` does not include +`sc-write-mode-enable-auto`, so add it yourself: ```toml copy [state-commit] -sc-write-mode = "migrate_evm" -sc-keys-to-migrate-per-block = 1024 +sc-write-mode-enable-auto = false +sc-write-mode = "flatkv_only" ``` -**`GIGA_MIGRATE_FROM_MEMIAVL`** is a Docker cluster environment variable for the -local devnet setup. When set to `true`, it boots every node in `memiavl_only` -mode, the v0 starting point for the FlatKV EVM migrate flow. It is mutually -exclusive with `GIGA_STORAGE`. If both are set, `GIGA_MIGRATE_FROM_MEMIAVL` -takes precedence. +A node whose history began in FlatKV, such as a node on a chain that +started in FlatKV, must pin `flatkv_only` as shown above. In `auto`, such a +node either fails every commit with a version-mismatch error or serves empty +reads from memiavl. + +A node pinned to `memiavl_only` does not take part in the migration. When +governance starts the migration, the pinned node diverges from the network. +Since v6.7.1, a node also refuses to start if a pinned `evm_migrated`, +`all_migrated_but_bank`, or `flatkv_only` mode disagrees with the migration +state on disk. The `cosmos_only` value from v6.4 and v6.5 `app.toml` files is +still accepted as an alias for `memiavl_only`. + +### Write modes + +| Mode | Meaning | +| --- | --- | +| `memiavl_only` | memiavl is the only SC backend. FlatKV is not created. | +| `migrate_evm` | Moves `evm/` keys from memiavl to FlatKV. | +| `evm_migrated` | The steady state after the `evm/` migration. | +| `migrate_all_but_bank` | Moves every remaining module except `bank/`. | +| `all_migrated_but_bank` | The steady state after that move. | +| `migrate_bank` | Moves the `bank/` module. | +| `flatkv_only` | FlatKV is the only SC backend. memiavl is not created. A node can state sync directly into this mode. | +| `auto` | Derives the mode from the migration state on disk. This is the default. | + +For the local Docker devnet, `GIGA_MIGRATE_FROM_MEMIAVL=true` starts every node +in `memiavl_only`, where the migration begins. Do not set it together with +`GIGA_STORAGE`. If you set both, `GIGA_MIGRATE_FROM_MEMIAVL` takes precedence. ```bash copy GIGA_MIGRATE_FROM_MEMIAVL=true make docker-cluster-start @@ -356,43 +287,14 @@ seidb migrate-evm-status --db-dir [--height ] `--db-dir` (short `-d`) points at the FlatKV data directory. `--height` selects a target version. The default, `0`, selects the latest available version. The -emitted JSON includes `migrate_evm_complete` (true after the migration finishes), +JSON includes `migrate_evm_complete` (true after the migration finishes), `migration_version`, `version_at`, and whether an in-flight boundary is still -present. Before you flip `sc-write-mode` to `evm_migrated`, poll the command -until `migrate_evm_complete` reports `true` on every validator. +present. When the migration finishes, each node also emits a `migration complete` summary log line and `seidb_migration_*` OpenTelemetry counters for the migrated keys and bytes. -### Inspecting hash log archives - -If you enable the per-block hash logger, the `seidb hashlog` command group -provides read-only tools for inspecting the on-disk hash log archives it -produces, so you can read or diff them without writing Go. - -- `seidb hashlog get-block ` prints every hash recorded for a - single block. A block with more than one record indicates it was executed more - than once (for example after a rollback), and each execution's hashes are - reported separately. Add `--json` to emit JSON instead of human-readable text. - - ```bash copy - seidb hashlog get-block [--json] - ``` - -- `seidb hashlog compare ` reports blocks whose hashes - differ between two archives — useful for pinpointing where two nodes diverged. - By default only the columns that differ are shown; pass `--full` to show every - column for each differing block. Restrict the comparison to a block range with - `--low` and `--high` (both must be supplied together), cap the number of - reported diffs with `--max-diffs` (`-1` for all), and add `--json` to emit - JSON. When the result is truncated at `--max-diffs`, the command warns that - there may be more differing blocks. - - ```bash copy - seidb hashlog compare [--low N --high M] [--max-diffs N] [--full] [--json] - ``` - ### Importing EVM state from memIAVL into FlatKV The `seidb import-flatkv-from-memiavl` command populates a FlatKV store from an existing memIAVL tree. It is for nodes that move to the FlatKV EVM commit store without a state sync. Two safety properties matter: @@ -456,9 +358,8 @@ In the `localnode` and `rpcnode` configuration scripts, setting precedence over the default. The supported receipt-store backends are `pebbledb` (also called `pebble`) and -`littidx`. The `littidx` backend stores receipt bodies in LittDB with a pebble -tag index for `eth_getLogs` filtering, honoring the `KeepRecent` and -`PruneIntervalSeconds` config for retention and pruning. The former `parquet` -option was removed. An unsupported value for `RECEIPT_BACKEND` (or -`rs-backend` in `app.toml`) is rejected with an error +`littidx`. For details on `littidx`, see the +[node operations guide](/node/node-operators). The former `parquet` option was +removed. The node rejects an unsupported value for `RECEIPT_BACKEND` (or +`rs-backend` in `app.toml`) with an error (`unsupported receipt-store backend "parquet"; supported: pebbledb, littidx`). diff --git a/node/node-operators.mdx b/node/node-operators.mdx index 7074a42..bd248f9 100644 --- a/node/node-operators.mdx +++ b/node/node-operators.mdx @@ -113,18 +113,16 @@ rs-backend = "pebbledb" The receipt-store backend accepts `pebbledb` (also called `pebble`) and -`littidx`. An unsupported value such as `rs-backend = "parquet"` (or -`RECEIPT_BACKEND=parquet`) is rejected with the error `unsupported receipt-store +`littidx`. The node rejects an unsupported value, such as `rs-backend = "parquet"` +(or `RECEIPT_BACKEND=parquet`), with the error `unsupported receipt-store backend "parquet"; supported: pebbledb, littidx`. -`littidx` is a LittDB-based backend that stores receipt bodies in LittDB's -immutable append-only segments alongside a PebbleDB tag index used to answer -`eth_getLogs` filters without scanning whole blocks. It honors the same -`KeepRecent` (derived from `min-retain-blocks`) and `prune-interval-seconds` -retention and pruning settings as the PebbleDB backend. Because receipt bodies -are buffered in memory and flushed periodically rather than written through a -WAL, a hard crash can lose the most recently written receipt bodies; this is an -accepted tradeoff for auxiliary, non-consensus RPC data. +`littidx` stores receipt bodies in LittDB and keeps a PebbleDB tag index. With +this index, `eth_getLogs` can filter logs without a scan of whole blocks. It uses +the same `min-retain-blocks` and `prune-interval-seconds` settings as `pebbledb`. +It flushes receipt bodies every few milliseconds and does not write them through +a WAL. A hard crash can therefore lose the newest receipt bodies. A read of a +lost receipt returns not found. In the localnode and rpcnode configuration scripts, if you enable Giga Storage (`GIGA_STORAGE=true`) and `RECEIPT_BACKEND` is not set, the receipt backend @@ -960,47 +958,6 @@ everything. Second, when `evm-ss-db-directory` is unset, nodes with an existing to `/data/state_store/evm/{backend}`. - - -The `[instrumentation] max-open-connections` field in `config.toml` is not a -socket limit despite its name. It is passed to promhttp as -`MaxRequestsInFlight` and caps the number of Prometheus scrapes served -concurrently; requests past the cap receive an HTTP `503` rather than being -queued. Keep the value above the number of scrapers that can overlap — an HA -Prometheus pair, blackbox probes, and the occasional human `curl` — because a -slow reader occupies a slot until it finishes or hits the write timeout. `0` -means unlimited. - -The Prometheus metrics HTTP server enforces a read timeout of 10s, a write -timeout of 30s, and an idle timeout of 60s. These bound how long a slow reader -can hold a request slot and how long idle keep-alive connections persist. - -Seed nodes also serve Prometheus metrics (for example the -`tendermint_p2p_peers` gauge) when instrumentation is enabled. Because a seed -serves no RPC, Prometheus is its only observability surface, so a failure to -bind the metrics listener is fatal and causes seed startup to fail — unlike a -full node, which logs the error and continues serving. - - -### Cosmos SDK state pruning - -Separate from the SeiDB retention settings above (`min-retain-blocks`, -`ss-keep-recent`), the node also honors the standard Cosmos SDK pruning options -(#3618). These control how much historical application state the baseapp keeps -and are applied to the node via `baseapp.SetPruning`: - -- `pruning` — the pruning strategy. Accepts `default`, `nothing` (archiving - node; keep all states), `everything` (keep only the most recent state), or - `custom` (use the `pruning-keep-recent` and `pruning-interval` values below). -- `pruning-keep-recent` — with `pruning = "custom"`, the number of recent - heights of state to keep. -- `pruning-interval` — with `pruning = "custom"`, how often (in blocks) pruning - is run. - -These can be supplied as `app.toml` settings or as the equivalent `seid` -start flags (e.g. `--pruning`, `--pruning-keep-recent`, `--pruning-interval`). - - @@ -1692,10 +1649,10 @@ historical data: execution and to compute the per-block app hash. Cosmos modules sit on a memory-mapped Merkle tree (`memiavl`) ported from Cronos. EVM state can also be routed through **FlatKV**, an EVM-tuned PebbleDB store - with per-type sub-databases (`account`, `code`, `storage`, `legacy`, - `metadata`). The `sc-write-mode` setting controls the routing, and its - default is `memiavl_only`. FlatKV is used only when `sc-write-mode` is set - to a migration mode or `flatkv_only`. + with per-type sub-databases (`account`, `code`, `storage`, and `misc`). + Since v6.7.0, nodes run in the `auto` write mode by default and move EVM + state into FlatKV when governance starts the migration. See the + [FlatKV EVM SC migration flow](/node/giga-storage-migration#flatkv-evm-sc-migration-flow). 2. **State Store (SS)**: versioned raw key-value pairs for historical queries. It is required for any node that serves RPC. The default backend is **PebbleDB**. Do not use RocksDB for new nodes. If you already use RocksDB, @@ -1729,29 +1686,16 @@ sc-snapshot-min-time-interval = 3600 # Cap on snapshot write throughput across all trees (MB/s). 0 = unlimited. sc-snapshot-write-rate-mbps = 100 -# EVM data routing between the Cosmos memiavl tree and FlatKV. -# Default: memiavl_only — all EVM state lives in memiavl. Valid values: -# memiavl_only, migrate_evm, evm_migrated, migrate_all_but_bank, -# all_migrated_but_bank, migrate_bank, flatkv_only, test_only_dual_write. -# Note: sc-read-mode and sc-enable-lattice-hash, which appeared in earlier -# versions of this page, were never seid config keys; if present in an -# existing app.toml they are silently ignored and can be removed. +# The node runs in the auto write mode by default and ignores sc-write-mode. +# To pin a mode, also set sc-write-mode-enable-auto = false. Read the Giga +# storage migration guide first. +# sc-read-mode, sc-enable-lattice-hash, and sc-keys-to-migrate-per-block are +# not seid config keys. The node ignores them, so you can remove them. sc-write-mode = "memiavl_only" -# How many EVM keys the in-flight migration (sc-write-mode = migrate_evm / -# migrate_bank / migrate_all_but_bank) drains from memiavl into FlatKV per -# block. Must be > 0; ignored when not in a migration mode. -sc-keys-to-migrate-per-block = 1024 - [state-commit.flatkv] -# FlatKV is the EVM-optimized commit store (PebbleDB). With the default -# sc-write-mode = "memiavl_only" it is not used and these settings have -# no effect. WAL crash recovery is idempotent, so fsync stays off by -# default. -fsync = false -async-write-buffer = 0 -snapshot-interval = 10000 -snapshot-keep-recent = 2 +# FlatKV uses built-in defaults. The only key in this section that the node +# applies is enable-read-write-metrics (default false). [state-store] # Historical SS layer. Validators can disable this to save disk; any node @@ -1855,10 +1799,8 @@ write amplification. evm-ss-split = true ``` -The SC layer has its own, separate migration path (`sc-write-mode` and the -FlatKV drain). It has different coordination requirements and its own -warnings. Do not combine the two flips in one restart. Before you change -`sc-write-mode`, see the +The SC layer has its own, separate migration path, which governance controls. +Before you change `sc-write-mode` or `sc-write-mode-enable-auto`, see the [FlatKV EVM SC migration flow](/node/giga-storage-migration#flatkv-evm-sc-migration-flow). Enabling Giga Storage requires a fresh state sync. If you flip `evm-ss-split` diff --git a/node/node-types.mdx b/node/node-types.mdx index 24d5b99..e78dee9 100644 --- a/node/node-types.mdx +++ b/node/node-types.mdx @@ -23,7 +23,7 @@ The `seid` binary uses these TCP ports. Change their settings to match your envi - `9090`: The default port for gRPC communication. This port is used for high-performance communication with the node. - `8545`: The default port for EVM HTTP RPC. This port is used for Ethereum JSON-RPC calls. It must be open if you want to interact with EVM-compatible applications. The [`frozen-rpc-router`](/node/technical-reference#frozen-rpc-router) binary also listens on `127.0.0.1:8545` by default. If you run the router on the same host as a live node, move one of them to a different port. The linked example keeps the router on `8545` and moves the two co-located nodes to `9545` and `9546`. - `8546`: The default port for EVM WebSocket RPC. EVM applications that need WebSocket connections use this port for real-time communication. -- `26660`: The default port for the Prometheus metrics endpoint, which you can use to monitor the environment. It is served only when instrumentation is enabled (`prometheus = true` under `[instrumentation]` in `config.toml`); in the default configuration it is not open. Seed nodes also serve Prometheus metrics when instrumentation is enabled — exposing p2p metrics such as `tendermint_p2p_peers` — which is their only observability surface since they serve no RPC. Note that on a seed node a failure to bind the Prometheus listener is fatal and crashes startup, whereas a full node logs the failure and continues. +- `26660`: The default port for the Prometheus metrics endpoint, which you can use to monitor the environment. The node opens it only when `prometheus = true` under `[instrumentation]` in `config.toml`. In the default configuration, this port is not open. Seed nodes serve it too. See [Seed node Prometheus metrics](/node/advanced-config-monitoring#seed-node-prometheus-metrics). You can change all of these ports in `$HOME/.sei/config/config.toml` and `$HOME/.sei/config/app.toml`. diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 12210a7..41688a7 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -36,28 +36,18 @@ seid tendermint show-validator seid query node info ``` +#### Experimental config manager (`SEI_CONFIG_MANAGER`) -#### Experimental config manager selection (`SEI_CONFIG_MANAGER`) +The experimental `SEI_CONFIG_MANAGER` environment variable selects how `seid` loads its configuration. The value must match exactly. -`seid` reads the experimental `SEI_CONFIG_MANAGER` environment variable to select which configuration manager resolves a node's configuration at startup. The value is matched exactly — it is not trimmed or case-folded. - -- Unset or `legacy`: uses the existing legacy config loader. This is the default and leaves the configuration path unchanged. -- `v2`: selects the sei-config-backed manager. This manager boots the node identically to the legacy path — it re-enters the legacy reader on your original `config.toml` and `app.toml`, and never rewrites those files or refuses boot. In addition, it runs an advisory config-validation pass that logs diagnostics at warn level (for example, a missing `chain.min_gas_prices`) without changing the boot outcome. Validation is non-fatal: a diagnostic is informational only. Because the pass reads the on-disk config before the node generates its own files, a brand-new node is not validated on its first boot; diagnostics first appear from the second start onward. -- Any other value: `seid` refuses to start and reports an `invalid SEI_CONFIG_MANAGER` error naming the legal tokens (unset, `legacy`, or `v2`). There is no silent fallback. +- Unset or `legacy`: the default config loader. +- `v2`: boots the node the same way as `legacy`, from the same `config.toml` and `app.toml`, and also logs config validation warnings. It never changes your files, and the node always starts. `v2` does not validate a new node on its first start, because its config files do not exist yet. +- Any other value: `seid` refuses to start with an `invalid SEI_CONFIG_MANAGER` error. ```bash -# Default behavior (legacy loader) — no variable set -seid start - -# Equivalent to the default -SEI_CONFIG_MANAGER=legacy seid start - -# Selects the v2 manager, which boots identically to legacy while emitting -# advisory config-validation warnings to the logs SEI_CONFIG_MANAGER=v2 seid start ``` - #### Freeze mode (`--freeze-height`) As of v6.6.3, you can put a full node into read-only freeze mode at a specified block height. Use the `--freeze-height` start flag or the corresponding `freeze-height` field in `app.toml`. Freeze mode exists for historical RPC nodes. A node frozen at an upgrade height keeps running the pre-upgrade binary and keeps serving the state that binary produced. It does not shut down at the boundary or execute the upgrade block with code that no longer matches that state. @@ -200,109 +190,81 @@ seidb evm-logical-digest --backend memiavl \ --memiavl-normalization translator ``` -Two backends match when the FlatKV `FINAL_DIGEST` equals the memIAVL `FINAL_DIGEST`. FlatKV also writes an internal migration-version marker row, which a memiavl-only node never owns. The command automatically omits that row from the final comparison. +Two backends match when the FlatKV `FINAL_DIGEST` equals the memIAVL `FINAL_DIGEST`. FlatKV can also hold two internal rows that a memiavl-only node never has: the migration-version marker and, during a migration, the migration boundary cursor. The command omits both rows from the final comparison. +The command accepts these flags: +- `--backend`: The backend to read: `flatkv`, `memiavl`, or `composite`. Use `composite` on a node in the middle of the migration. It digests the union of the FlatKV and memIAVL rows, so you can compare the node with a memiavl-only node at the same height. +- `--db-dir` (`-d`): For FlatKV, the FlatKV data directory. For memIAVL, the memIAVL root directory that contains `current/` and `snapshot-*`. Required unless you use `composite`. +- `--flatkv-dir` and `--memiavl-dir`: The two data directories for `--backend composite`. You must set both in that mode. +- `--height`: The target version. FlatKV WAL-replays to it, and memIAVL resolves `snapshot-/evm` (`0` selects the `current` symlink). +- `--memiavl-open-mode`: How the command reads memIAVL. `snapshot` (the default) scans the snapshot file at `--height`. It is the fastest mode, but it needs a snapshot at that height, or `--height 0`. `changelog` reads the nearest snapshot at or below `--height` and adds the changelog's EVM writes up to `--height`. `replay` opens the database, replays the changelog to `--height`, and walks the tree. It is about 10 times slower than `snapshot`. `changelog` and `replay` return the same rows. Use one of them when no snapshot exists at `--height`. +- `--memiavl-normalization`: The memIAVL normalization mode. Use `semantic` or `independent` for the raw EVM key-value decoder, or `translator` for the current migration mapping. The default is `semantic`. +- `--json`: Print the digest or inspect report as one JSON object on stdout, and the progress messages on stderr. +- `--inspect-bucket`: Inspect one normalized bucket (`account`, `code`, `storage`, or `misc`), not the global digest. Inspect mode with `--memiavl-normalization translator`, or with `--details` on the `storage` bucket, requires `--memiavl-open-mode snapshot`. +- `--inspect-plan` (inspect mode): A JSON file that lists several inspect reports to produce from one scan. It replaces the single-report inspect flags. +- `--key-offset` (inspect mode): The byte offset into the physical key, applied before `--key-prefix` or sharding. +- `--key-prefix` (inspect mode): A hex prefix, relative to `--key-offset`, that filters physical keys. +- `--shard-next-bytes` (inspect mode): Group matching keys by this many bytes after `--key-prefix`. +- `--list` (inspect mode): List pairs of matching keys and logical values instead of shard `bucket_digest` values. +- `--list-limit` (inspect mode): The maximum number of pairs to print with `--list` (default `1000`). A value `<= 0` means unlimited. +- `--details` (inspect list mode): Include backend-specific version metadata. +- `--find-hash`: An optional 32-byte hex per-entry hash to search for. When two `bucket_digest` values differ by exactly one entry, their XOR is the hash of that entry. This flag prints every matching entry, so you can locate a single diverging row. #### Dumping FlatKV state and verifying its lattice hash -The `dump-flatkv` subcommand dumps every physical `(key, value)` pair of a FlatKV store into per-bucket files, formatted to match `dump-iavl` so the same diff tooling works on both. It can optionally also compute the per-bucket and total LtHash (lattice hash) over the scanned state and verify that total against the committed root recorded in snapshot metadata. +`dump-flatkv` writes every physical key-value pair of a FlatKV store to one file per bucket. The format matches `dump-iavl`, so the same diff tools work on both. The command can also compute the lattice hash (LtHash) of each bucket and of the whole store. Then it compares the total with the committed value in the snapshot metadata. -The command opens an independent read-only clone of the store (the snapshot is hard-linked and the changelog WAL is replayed into a temporary directory under the data dir), so it is safe to run against a live, block-producing node. The scan is throttled by `--read-limit-mb` so a dump against a running node does not starve the chain of disk bandwidth. +The command reads from a temporary read-only copy of the store, so you can run it against a live node. `--read-limit-mb` limits its disk reads, so it does not slow the node down. ```bash -# Latest version, all buckets, default 64 MiB/s throttle, LtHash + verify. -seidb dump-flatkv -d /.sei/data/state_commit/flatkv -o /tmp/flatkv-dump +# Latest version, all buckets, default 64 MiB/s read limit, LtHash check. +seidb dump-flatkv -d $HOME/.sei/data/state_commit/flatkv -o /tmp/flatkv-dump -# Pin a specific height. -seidb dump-flatkv -d /.sei/data/state_commit/flatkv -o /tmp/flatkv-dump \ +# A specific height. +seidb dump-flatkv -d $HOME/.sei/data/state_commit/flatkv -o /tmp/flatkv-dump \ --height 216890000 -# Offline / idle disk: go full speed, skip LtHash. -seidb dump-flatkv -d /.sei/data/state_commit/flatkv -o /tmp/flatkv-dump \ +# Idle disk: no read limit and no LtHash. +seidb dump-flatkv -d $HOME/.sei/data/state_commit/flatkv -o /tmp/flatkv-dump \ --read-limit-mb 0 --lthash=false -# Verify the FlatKV lattice hash only, without writing key/value dump files. -seidb dump-flatkv -d /.sei/data/state_commit/flatkv --lthash-only +# Check the LtHash only, without writing dump files. +seidb dump-flatkv -d $HOME/.sei/data/state_commit/flatkv --lthash-only ``` The command accepts these flags: -- `--db-dir` (`-d`): The FlatKV data directory (the directory containing `current/`, `snapshot-*`, and `changelog/`). Required. -- `--output-dir` (`-o`): Where to write the per-bucket dump files (one file per bucket). Required unless `--lthash-only` is set. -- `--height`: The target version. `0` (the default) selects the latest available version by replaying the WAL to the tip. -- `--bucket` (`-b`): Restrict the on-disk dump to a single bucket (`account`, `code`, `storage`, or `legacy`). The default is all buckets. This only filters which hex files are written; the full keyspace is always scanned, and `--lthash` always covers all four buckets, so the LtHash total stays valid. -- `--lthash`: Compute the per-bucket and total LtHash over the scanned state and verify the total against committed snapshot metadata. Default `true`. The LtHash is always computed over all buckets regardless of `--bucket`, so the total matches the node's committed LtHash. -- `--lthash-only`: Compute and verify the LtHash without writing any bucket dump files. Default `false`. It requires `--lthash=true`, cannot be combined with `--bucket`, and does not require `--output-dir`. -- `--read-limit-mb`: Throttle the scan to at most this many MiB/s of `(key+value)` bytes read. Default `64`. A value of `0` disables throttling. Keep it low (default or less) on a shared or live node; raise it only for offline runs on idle disks. Negative values are rejected. - -With `--lthash`, the command prints an `LtHash (lattice hash)` block listing each bucket's count and checksum and the `TOTAL`, followed by a verification line comparing the re-scanned total against the committed snapshot metadata. A match prints `PASS`; a mismatch prints `FAIL` and the command exits non-zero. Verification is skipped when the selected snapshot predates LtHash metadata (its committed hash then covers only replayed WAL deltas, not full state) or when no committed LtHash is recorded at that version. - +- `--db-dir` (`-d`): The FlatKV data directory, which contains `current/`, `snapshot-*`, and `changelog/`. Required. +- `--output-dir` (`-o`): The directory for the dump files, one file per bucket. Required unless you set `--lthash-only`. +- `--height`: The target version. The default, `0`, selects the latest version. +- `--bucket` (`-b`): Write only one bucket: `account`, `code`, `storage`, or `misc`. The command still scans every bucket, so the LtHash total stays valid. +- `--lthash`: Compute the LtHash and check it against the snapshot metadata. The default is `true`. +- `--lthash-only`: Check the LtHash, but do not write dump files. It requires `--lthash=true`. Do not use it with `--bucket`. +- `--read-limit-mb`: The maximum read speed in MiB/s. The default is `64`, and `0` turns the limit off. Raise it only for offline runs. +With `--lthash`, the output lists the count and checksum of each bucket, the `TOTAL`, and a `PASS` or `FAIL` line. On `FAIL`, the command exits with a non-zero code. The command skips the check when the snapshot has no committed LtHash to compare with. #### Inspecting hash log archives -The `hashlog` command group provides read-only tools for inspecting the on-disk hash log archives produced by the hashlogger. Use it to pull the hashes recorded for a single block or to diff two archives without writing any Go code. - -##### Printing a single block's hashes +The `seidb hashlog` commands read the archives that the [per-block hash logger](/node/advanced-config-monitoring#per-block-hash-logger) writes. They do not change the archives. -The `get-block` subcommand prints every hash recorded for a single block in a hash log archive. +`get-block` prints every hash recorded for one block. If the node executed the block more than once, for example after a rollback, the command prints each execution separately. A hash type that was not recorded for the block prints as ``, or as `null` in JSON. ```bash -# Print the hashes recorded for a block in an archive -seidb hashlog get-block - -# Emit JSON instead of human-readable text -seidb hashlog get-block --json +seidb hashlog get-block [--json] ``` -If the block was executed more than once (for example, after a rollback that replayed it), the archive holds several records for that block and each execution's hashes are reported separately. A hash type that was registered but not recorded for the block prints as `` (or serializes to JSON `null`, which is distinguishable from an absent type). - -##### Comparing two archives - -The `compare` subcommand compares two hash log archives and reports the blocks whose hashes differ between them. +`compare` lists the blocks whose hashes differ between two archives, which shows where two nodes diverged. If the archives match over the compared range, the command reports that. ```bash -# Compare two archives over their full range -seidb hashlog compare - -# Restrict the comparison to a block range (both flags required together) -seidb hashlog compare --low --high - -# Cap the number of differing blocks reported -seidb hashlog compare --max-diffs - -# Show every column for each differing block, and emit JSON -seidb hashlog compare --full --json +seidb hashlog compare [--low --high ] [--max-diffs ] [--full] [--json] ``` -The command accepts these flags: - -- `--low`: The lowest block to compare (inclusive). Requires `--high`. -- `--high`: The highest block to compare (inclusive). Requires `--low`. The `--low` and `--high` flags are optional, but must be supplied together; a one-sided range fails with an error. -- `--max-diffs`: The maximum number of differing blocks to report. The default is `-1`, which reports all of them. When the output is truncated at the cap, the command warns that there may be more differing blocks. -- `--full`: Show every column of every record for each differing block. The default is a compact view that shows only the columns that differ. This is also the only sensible rendering when the record counts differ between the two sides (a rollback re-executed the block a different number of times), since there is no single pair of records to diff column by column. -- `--json`: Emit JSON instead of human-readable text. The compact-by-default and `--full` column filtering apply to JSON output as well. - -When the archives are identical over the compared range, the command reports that and exits. - -The command accepts these flags: - -- `--backend`: The backend to read (`flatkv`, `memiavl`, or `composite`). Use `composite` to digest the union of FlatKV and memIAVL rows for a node that is mid-migration, so it can be compared against a memiavl-only node at the same height. -- `--db-dir` (`-d`): For FlatKV, the FlatKV data directory. For memIAVL, the memIAVL root directory that contains `current/` and `snapshot-*`. Required unless `--backend composite` is used (composite mode uses `--flatkv-dir` and `--memiavl-dir` instead). -- `--flatkv-dir`: The FlatKV data directory in `composite` mode. Required with `--backend composite`. -- `--memiavl-dir`: The memIAVL root directory (containing `current/` and `snapshot-*`) in `composite` mode. Required with `--backend composite`. -- `--height`: The target version. FlatKV WAL-replays to it, and memIAVL resolves `snapshot-/evm` (`0` selects the `current` symlink). -- `--memiavl-open-mode`: How memIAVL is read. `snapshot` (the default) is the fast path: it sequentially scans the completed snapshot kvs file and requires an on-disk snapshot at `--height` (or `--height 0` for the `current` symlink). `replay` is the slow path (roughly an order of magnitude slower): it opens a read-only DB, replays the changelog up to `--height`, then walks the mmap tree. Use `replay` only when no snapshot exists at the target height. Prefer `snapshot` whenever `--height` matches an existing snapshot boundary. -- `--memiavl-normalization`: The memIAVL normalization mode. Use `semantic` or `independent` for the raw EVM key-value decoder, or `translator` for the current migration mapping. The default is `semantic`. -- `--inspect-bucket`: Inspect one normalized bucket (`account`, `code`, `storage`, or `legacy`) instead of printing the global digest. It supports only `--memiavl-open-mode=snapshot`; combining it with `replay` returns an error. -- `--key-offset` (inspect mode): The byte offset into the physical key, applied before `--key-prefix` or sharding. -- `--key-prefix` (inspect mode): A hex prefix, relative to `--key-offset`, that filters physical keys. -- `--shard-next-bytes` (inspect mode): Group matching keys by this many bytes after `--key-prefix`. -- `--list` (inspect mode): List pairs of matching keys and logical values instead of shard `bucket_digest` values. -- `--list-limit` (inspect mode): The maximum number of pairs to print with `--list` (default `1000`). A value `<= 0` means unlimited. -- `--details` (inspect list mode): Include backend-specific version metadata. -- `--find-hash`: An optional 32-byte hex per-entry hash to search for. When two `bucket_digest` values differ by exactly one entry, their XOR is the hash of that entry. This flag prints every matching entry, so you can locate a single diverging row. +- `--low` and `--high`: Compare only this block range (inclusive). Set both or neither. +- `--max-diffs`: The maximum number of blocks with differences to report. The default, `-1`, reports all of them. If the output stops at the limit, the command warns that more blocks can have differences. +- `--full`: Show every column for each block with differences. By default, the command shows only the columns that differ. Use `--full` when the archives hold a different number of records for a block. +- `--json`: Print JSON instead of text. ### Autobahn (GigaRouter) config generation @@ -325,18 +287,14 @@ seid tendermint gen-autobahn-config ./node0 ./node1 --output autobahn.json --per seid tendermint gen-autobahn-config ./node0 ./node1 --output autobahn.json --blockdb-retention 30s --blockdb-gc-period 10s ``` -The `--persistent-state-dir` flag controls where Autobahn persists its consensus state and BlockDB across restarts. The default is `data/autobahn`, so persistence is enabled by default without any operator action. Autobahn's durable block and quorum-certificate storage now lives in a LittDB-backed BlockDB opened under `/blockdb`, replacing the previous data write-ahead logs (the old `globalblocks/` and `fullcommitqcs/` subdirectories are no longer read). At config load time, a relative path is resolved against the node's `--home` directory, and an absolute path is used as is. An empty value (`--persistent-state-dir=`) disables persistence entirely and both the consensus and data layers run in memory only (memblock). When set, the flag populates the `PersistentStateDir` field in the generated config. +The `--persistent-state-dir` flag controls where Autobahn persists its consensus state and BlockDB across restarts. The default is `data/autobahn`, so persistence is on by default. The node resolves a relative path against its `--home` directory. An empty value (`--persistent-state-dir=`) turns persistence off, and Autobahn keeps everything in memory. If you set the flag, it fills the `PersistentStateDir` field in the generated config. - - Operators upgrading from a release that used the data WALs must be aware that Autobahn consensus state now lives under `/blockdb` (a LittDB BlockDB). The old `globalblocks/` and `fullcommitqcs/` WAL directories are no longer read. - +Autobahn stores blocks and commit quorum certificates in a LittDB-based BlockDB under `/blockdb`. Since v6.7.0, it no longer reads the old `globalblocks/` and `fullcommitqcs/` write-ahead log directories. -Two additional flags tune the BlockDB: +Two more flags tune the BlockDB. When you set either flag, the command writes a `block_db` section into `autobahn.json`: -- `--blockdb-retention`: Sets the BlockDB retention TTL written into the `block_db` section of the generated config. The default is `30s` because this helper targets local/docker clusters rather than production node bring-up. Pass an empty value (`--blockdb-retention=`) to omit the field and keep littblock's production default of 24h. -- `--blockdb-gc-period`: Sets the BlockDB garbage-collection period (for example `10s`). Omit it to keep littblock's default GC period. - -When either flag is set, an optional `block_db` section is written into `autobahn.json`: +- `--blockdb-retention`: How long the BlockDB keeps data. The default is `30s`, because this command targets local and Docker clusters. Pass an empty value (`--blockdb-retention=`) to omit the field and use the BlockDB default of 24 hours. +- `--blockdb-gc-period`: How often the BlockDB runs garbage collection, for example `10s`. If you omit it, the BlockDB default applies. ```json "block_db": { @@ -345,7 +303,7 @@ When either flag is set, an optional `block_db` section is written into `autobah } ``` -Each field is independently optional and is omitted from the JSON when empty; absent fields keep whatever littblock's default config uses. The `block_db` section overlays those defaults only when `persistent_state_dir` is set, and is ignored when persistence is disabled (memblock). When set, `retention` and `gc_period` must each be greater than zero. +Both fields are optional. If you set a field, its value must be greater than zero. Autobahn ignores `block_db` when persistence is off. The command reads these files from each node directory: @@ -395,27 +353,13 @@ Any message whose fields exceed these limits is rejected at decode time, so an o Giga replaces the CometBFT mempool, so Giga does not support the `unsafe_flush_mempool` RPC endpoint. The endpoint returns `unsafe_flush_mempool is not supported with autobahn mempool`. - - #### HashVault app-hash equivocation guard -When a node runs under Autobahn (GigaRouter), the GigaRouter builds and owns an app-hash equivocation guard called HashVault. As each finalized height is executed, HashVault records that height's committed app hash before the implied state is committed. If the node ever attempts to commit a different app hash for a height it has already finalized, HashVault detects the conflict and halts the node. This protects a validator against externalizing two different app hashes for the same height, which could otherwise lead to slashing. - -HashVault is enabled by default on both Autobahn validators and fullnodes. It stores committed app hashes in a durable Pebble DB under the Autobahn persistent state directory, at `/hashvault` (see the `--persistent-state-dir` flag and the `PersistentStateDir` config field above). When Autobahn runs in memory only (no persistent state directory, for example in tests), HashVault falls back to a no-op with no equivocation protection. - - - If a node hits a startup panic reporting a HashVault app-hash mismatch, **do not restart it without human investigation** — the node attempted to change its mind about an already-finalized height. The panic and the preceding HashVault error log the conflicting hashes and the on-disk HashVault data directory (`hashVaultDir`). - - Only if you are certain there is no real equivocation, you can recover by stopping the node, deleting the HashVault data directory shown in the panic message, and restarting. **This removes equivocation protection**: if the node then commits a conflicting hash for an already-finalized height, the validator may be slashed. - +Under Autobahn, validators and fullnodes run HashVault by default. HashVault records the app hash of each finalized height before the node commits the state. If the node later tries to commit a different app hash for the same height, HashVault halts the node. Without HashVault, a validator can commit two different app hashes for one height. This can cause slashing. -You can disable HashVault entirely with the top-level `hash-vault-disabled-unsafe` field in `config.toml` (default `false`). This is an explicit, last-resort operator decision to run **without** equivocation protection; a node started with it enabled logs error-level warnings on every startup. Prefer the recovery steps above — only set `hash-vault-disabled-unsafe = true` if you are very sure the stored hashes are wrong and you keep hitting the same panic on new blocks. +HashVault stores the hashes in a Pebble database at `/hashvault`. When Autobahn runs in memory only, HashVault gives no protection and logs an error at startup. -```toml -# config.toml — top-level key, before any [section] header. -# Leave false to keep equivocation protection enabled (recommended). -hash-vault-disabled-unsafe = false -``` +If HashVault halts a node, follow [HashVault app-hash equivocation panic](/node/troubleshooting#hashvault-app-hash-equivocation-panic) before you restart it. As a last resort, the top-level `hash-vault-disabled-unsafe` key in `config.toml` turns HashVault off. Keep the default, `false`. ### Key management @@ -497,23 +441,11 @@ ss-backend = "pebbledb" ss-keep-recent = 100000 ss-prune-interval = 600 -# Base-layer (IAVL/baseapp) pruning. These Cosmos SDK pruning settings are read -# from the start flags/config and applied to the node's baseapp, so they are -# honored when the node runs. `pruning` selects a strategy: "default", "nothing" -# (archive node, keep all states), "everything" (keep only recent states), or -# "custom" (use the pruning-keep-recent and pruning-interval values below). -pruning = "default" -# Number of recent heights to keep on disk. Used with pruning = "custom". -pruning-keep-recent = "0" -# How often (in blocks) to run a pruning pass. Used with pruning = "custom". -pruning-interval = "0" - # gRPC server configuration [grpc] enable = true address = "0.0.0.0:9090" -# Maximum message size in bytes the server can receive. Bounds per-request memory -# allocation before the rate limiter fires. Default 4 MB (4194304). +# Maximum message size, in bytes, that the server accepts. Default 4 MiB. max-recv-msg-size = 4194304 # Maximum number of simultaneous open connections. 0 means unlimited. max-open-connections = 1000 @@ -521,15 +453,15 @@ max-open-connections = 1000 max-connection-idle = "5m0s" # Maximum duration a connection may exist before it is closed. 0 means infinity. max-connection-age = "0s" -# Additive grace period after max-connection-age during which the connection is -# forcibly closed. 0 means infinity. +# Extra time after max-connection-age before the server closes the connection. +# 0 means infinity. max-connection-age-grace = "0s" # Interval after which, with no activity, the server pings the client for liveness. keepalive-time = "2h0m0s" # Duration the server waits for a keepalive ping ack before closing the connection. keepalive-timeout = "20s" -# Minimum interval a client must wait between keepalive pings; more frequent pings -# are penalized. +# Minimum interval between client keepalive pings. +# The server penalizes clients that ping more often. keepalive-min-time = "5m0s" # Whether the server allows keepalive pings even when there are no active streams. keepalive-permit-without-stream = false @@ -538,7 +470,7 @@ keepalive-permit-without-stream = false - The `[grpc]` server applies bounded defaults even when these keys are absent from an older `app.toml`. A node upgrading with a config file that predates these fields gets the in-code defaults (for example, a 4 MB `max-recv-msg-size`, `1000` `max-open-connections`, and a `5m` `max-connection-idle`) rather than running with unlimited connections or message sizes. A negative duration override for a keepalive or connection-age field is treated as a misconfiguration and clamped back to its safe default. The `max-connection-age` and `max-connection-age-grace` fields default to `0` (gRPC's "infinity"), and the keepalive-time/timeout/min-time defaults mirror gRPC's own defaults, so they are opt-in and do not change behavior unless configured. + If an older `app.toml` does not have these `[grpc]` keys, the node uses the defaults shown above, not unlimited values. A negative keepalive or connection-age duration falls back to its default. ### Config.toml parameters @@ -588,15 +520,11 @@ timeout-broadcast-tx-commit = "10s" # Maximum number of results returned by tx_search and block_search. # Set to 0 to disable the cap (not recommended on public nodes). max-tx-search-results = 10000 -# max-search-scan-budget sets a process-wide cap on the total number of KV index -# entries that all in-flight tx_search and block_search requests may visit at -# once. It is shared across requests (not applied per-query) and bounds peak -# memory and scan CPU under broad or highly concurrent search load. When the -# budget is exhausted, in-flight searches fail with "kv indexer scan budget -# exceeded; narrow the query or retry later" rather than continuing to accumulate. -# The same budget is shared by the tx and block indexers, so the cap is -# process-wide across both. Default 100000; set to 0 to disable the cap (not -# recommended on public nodes). +# Maximum number of KV index entries that all running tx_search and +# block_search requests may scan at once. The budget is shared by both +# indexers. When it runs out, searches fail with "kv indexer scan budget +# exceeded; narrow the query or retry later". Set to 0 to disable the cap +# (not recommended on public nodes). max-search-scan-budget = 100000 # Mempool Configuration @@ -667,6 +595,18 @@ Oracle Parameters: These values reflect the current on-chain parameters. For the source of truth, query them directly with `seid query staking params` and `seid query slashing params`. Per-validator settings (for example, commission rate and commission max change rate) are configured per validator and are not chain-level parameters. +## Build tags + +The `historical_replay` build tag makes a `seid` binary to replay old blocks. This binary accepts transaction bodies with non-canonical encoding. Normal binaries reject these bodies. The replay binary can therefore execute blocks from before that check existed. + +```bash +make install BUILD_TAGS=historical_replay +``` + + +A `historical_replay` binary is not safe for consensus. Do not use it to validate, produce blocks, or serve live traffic. Normal builds do not include the lenient decoder. + + ## File locations Understanding the purpose and location of important files helps with maintenance @@ -702,21 +642,3 @@ $HOME/.sei/ This reference guide gives essential technical information for operating Sei nodes and validators. For API documentation and other detailed specifications, see the relevant sections of this documentation. - - - -## Build tags - -### `historical_replay` - -The `historical_replay` build tag produces a consensus-unsafe `seid` variant intended solely for replaying historical blocks. When you build with this tag, the block-execution transaction decoder is swapped for a lenient protobuf decoder (`NewTxConfigWithoutBodyBloatRejection`) that does **not** reject non-canonical (body-bloat) transaction bodies. This lets a node decode and execute historical blocks whose transaction bodies predate strict body-bloat rejection. - -```bash -# Build a historical-replay seid variant -go build -tags historical_replay ... -``` - - - A binary built with the `historical_replay` tag is **consensus-unsafe** and must only be used for historical replay. Never run it on live or production paths. The lenient decoder is compiled in only when the tag is present, so an untagged (production) binary can never reach it — the lenient execution decoder stays off every mempool, `CheckTx`, and `DeliverTx` path. Do not use a `historical_replay` build to validate, produce blocks, or serve live traffic. - - diff --git a/node/troubleshooting.mdx b/node/troubleshooting.mdx index e6191a1..45327b0 100644 --- a/node/troubleshooting.mdx +++ b/node/troubleshooting.mdx @@ -111,17 +111,7 @@ systemctl restart seid Nodes created before the storage layout change keep the FlatKV store at the legacy path `$HOME/.sei/data/flatkv` instead of `$HOME/.sei/data/state_commit/flatkv`. Pass whichever directory exists on your node to `--db-dir`. -The `dump-flatkv` command accepts these flags: - -- `--db-dir` (`-d`): The FlatKV database directory. -- `--output-dir` (`-o`): The output directory, with one file for each bucket. Required unless `--lthash-only` is set. -- `--height`: The FlatKV target version. The default, `0`, selects the latest available version. -- `--bucket` (`-b`): Restrict the dump to a single bucket (`account`, `code`, `storage`, or `legacy`). The default is all buckets. This only filters which files are written; the full keyspace is always scanned, and the LtHash always covers all four buckets. -- `--lthash`: Also compute the per-bucket and total LtHash (lattice hash) over the scanned state, and verify the total against the committed root recorded in snapshot metadata. The default is `true`. A mismatch exits non-zero. -- `--lthash-only`: Compute and verify the LtHash without writing any bucket dump files. Requires `--lthash=true`, and does not require `--output-dir`. The default is `false`. -- `--read-limit-mb`: Throttle the scan to at most this many MiB/s of (key+value) bytes read, so a dump against a running node does not starve the chain of disk bandwidth. The default is `64`. Set `0` for unlimited. Keep it at the default or lower on a shared/live node; raise it only for offline runs on idle disks. - -Because `dump-flatkv` opens an independent read-only clone of the store (the snapshot is hard-linked and the changelog WAL is replayed into a temp directory), it is safe to run against a live, block-producing node, and the `--read-limit-mb` throttle prevents it from starving the node of disk bandwidth. +`dump-flatkv` reads from a temporary copy of the store, so you can also run it against a live node. For all flags, see [Dumping FlatKV state](/node/technical-reference#dumping-flatkv-state-and-verifying-its-lattice-hash) in the technical reference. For example, to dump only the `storage` bucket at a specific version: @@ -129,19 +119,13 @@ For example, to dump only the `storage` bucket at a specific version: seidb dump-flatkv --db-dir $HOME/.sei/data/state_commit/flatkv --output-dir /home/ubuntu/flatkv-dump --height 12345678 --bucket storage ``` -To verify the FlatKV lattice hash against the committed snapshot metadata without writing any key/value dump files: +To check only the FlatKV lattice hash against the snapshot metadata, with no dump files: ```bash seidb dump-flatkv --db-dir $HOME/.sei/data/state_commit/flatkv --lthash-only ``` -For an offline run on an idle disk, go full speed and skip the LtHash: - -```bash -seidb dump-flatkv --db-dir $HOME/.sei/data/state_commit/flatkv --output-dir /home/ubuntu/flatkv-dump --read-limit-mb 0 --lthash=false -``` - -With `--lthash`, the output prints a per-bucket count and checksum plus a `TOTAL` LtHash, followed by a `LtHash verification vs snapshot metadata` PASS/FAIL line. Verification is skipped (rather than failing) when the selected snapshot predates LtHash metadata, since the committed hash then covers only replayed WAL deltas rather than full state. +The output ends with a `PASS` or `FAIL` line for the lattice hash check. On `FAIL`, the command exits with a non-zero code. ### Comparing EVM state between memIAVL and FlatKV @@ -159,29 +143,24 @@ seidb evm-logical-digest --backend memiavl \ --db-dir $HOME/.sei/data/state_commit/memiavl --height 213200000 ``` -The `--backend` flag accepts `flatkv`, `memiavl`, and `composite`. - -For memIAVL, `--memiavl-open-mode` controls how leaves are read: - -- `snapshot` (the default) sequentially scans the completed snapshot `kvs` file at `snapshot-/evm`. This is the fast path and requires an on-disk snapshot at that exact height (or `--height 0` for the current symlink). Prefer it whenever your target height matches an existing snapshot boundary. -- `replay` opens a read-only DB, replays the changelog up to `--height`, then walks the in-memory/mmap tree. It is roughly an order of magnitude slower than `snapshot`, so use it only when no snapshot exists at the target height (for example, when a node's snapshot rewrite lags the tip): +The memIAVL digest above needs a snapshot at the target height. If no snapshot exists at that height, add `--memiavl-open-mode changelog`. This mode reads the nearest older snapshot and applies the changelog up to the height. The `replay` mode returns the same rows, but it is about 10 times slower. ```bash -# memIAVL digest for a height with no on-disk snapshot (slower): -seidb evm-logical-digest --backend memiavl --memiavl-open-mode replay \ +# memIAVL digest for a height with no on-disk snapshot: +seidb evm-logical-digest --backend memiavl --memiavl-open-mode changelog \ --db-dir $HOME/.sei/data/state_commit/memiavl --height 213205000 ``` -On a node that is mid-migration, EVM state is split between the two backends: FlatKV holds the rows already migrated, while memIAVL still holds the rows not yet past the migration boundary. Use `--backend composite` to digest the union of both, so a migrating node can be compared against a memIAVL-only node at the same height. In `composite` mode, `--db-dir` is not required; instead provide the two backend directories with `--flatkv-dir` and `--memiavl-dir`. Because a live migrating node usually keeps memIAVL snapshots at heights outside FlatKV's retained window, pass `--memiavl-open-mode replay`: +On a node in the middle of the migration, EVM state is split between FlatKV and memIAVL. Use `--backend composite` to digest both together, so you can compare the node with a memIAVL-only node at the same height. Pass the two directories with `--flatkv-dir` and `--memiavl-dir` instead of `--db-dir`: ```bash # Mid-migration node: digest the flatkv + memiavl union at one height: -seidb evm-logical-digest --backend composite --memiavl-open-mode replay \ +seidb evm-logical-digest --backend composite --memiavl-open-mode changelog \ --flatkv-dir $HOME/.sei/data/state_commit/flatkv \ --memiavl-dir $HOME/.sei/data/state_commit/memiavl --height 213200000 ``` -Each run prints per-bucket `bucket_digest` values and a single `FINAL_DIGEST` line that covers the `account`, `code`, `storage`, and `legacy` buckets. Compare the `FINAL_DIGEST` lines from both backends at the same height. They should match. FlatKV can contain FlatKV-only migration marker rows that a memIAVL-only node never owns: the migration-version marker (present once a migration completes) and the migration-boundary cursor (present only while a migration is in flight). The command automatically omits both rows from the final result, so memIAVL, mid-migration, and completed nodes all produce comparable digests. +Each run prints per-bucket `bucket_digest` values and a single `FINAL_DIGEST` line that covers the `account`, `code`, `storage`, and `misc` buckets. Compare the `FINAL_DIGEST` lines from both backends at the same height. If the EVM state is the same, the lines match. The command leaves out FlatKV's internal migration rows, so memIAVL, mid-migration, and migrated nodes all produce comparable digests. For targeted debugging, the command can also inspect a single normalized bucket instead of printing the global digest. The [seidb tooling section of the technical reference](/node/technical-reference#comparing-evm-state-across-backends) has the full flag reference, including inspect mode, sharding, and `--find-hash`. These examples list the first 50 `account` rows with version metadata, and shard the `storage` bucket under a key prefix by the next 2 bytes: @@ -474,13 +453,11 @@ This means that you did not shut down the node properly. In that case, try to sh Then try the rollback steps again. - - ## HashVault app-hash equivocation panic -Autobahn validators and fullnodes run an app-hash equivocation guard called HashVault, enabled by default. It stores the app hashes the node has committed in a durable database under `/hashvault`. If the node is ever about to commit a different app hash for a height it has already finalized, HashVault halts the node to prevent it from "changing its mind" about a finalized block. +Under Autobahn, HashVault halts a node that tries to commit a different app hash for a height it already finalized. For background, see [HashVault](/node/technical-reference#hashvault-app-hash-equivocation-guard). -When the guard trips, you will see a panic in the logs similar to: +Before the node halts, it logs an error like this: ```text Hashvault detected app hash mismatch; node attempted to change its mind. DO NOT RESTART WITHOUT HUMAN INVESTIGATION. ... @@ -488,28 +465,28 @@ blockHeight=[HEIGHT] existingHex=[...] incomingHex=[...] hashVaultDir=[/path/to/ ``` -**Do not restart the node without human investigation.** A HashVault mismatch can indicate a genuine equivocation. Restarting blindly, or removing the guard, can cause your validator to externalize a conflicting hash for an already-finalized height, which risks **slashing**. +Do not restart the node before you investigate. A mismatch can mean a real equivocation, for example the same validator key on two machines. If you restart the node or remove the guard, your validator can commit a conflicting hash for a finalized height. This can cause slashing. -**Investigate first.** Determine whether the mismatch reflects a real equivocation (for example, the same validator key running on more than one machine, or committed state that diverged from what the vault recorded) or a benign situation such as an out-of-band rollback or restore that left the committed app state inconsistent with the vault's history. +Find out if the mismatch is a real equivocation. A rollback or restore outside the normal process can also leave the app state out of sync with the stored hashes. -### Recovery (only if you are certain there is no real equivocation) +### Recovery -If, and only if, you are certain the stored hashes are wrong and there is no real equivocation, you can bypass the guard: +Only if you are sure that there is no real equivocation: -1. **Stop the node.** -2. **Delete the HashVault data directory** shown as `hashVaultDir` in the panic message (by default `/hashvault`). -3. **Restart the node.** It starts with an empty equivocation history and re-commits hashes as it re-executes blocks. +1. Stop the node. +2. Delete the HashVault directory shown as `hashVaultDir` in the log (by default `/hashvault`). +3. Restart the node. It starts with an empty hash history. -Deleting this directory removes equivocation protection for the heights it covered. If the node then commits a conflicting hash for a height it has already finalized, the validator may be slashed. +Deleting the directory removes the protection for the heights it covered. -### Last-resort: disabling HashVault +### Disabling HashVault -If you repeatedly hit the same panic on new blocks and are very sure the stored hashes are totally wrong, you can run the node with the guard disabled by setting the top-level `hash-vault-disabled-unsafe` field in `config.toml`: +If the same panic occurs again on new blocks, and you are sure that the stored hashes are wrong, you can turn off HashVault. Set the top-level `hash-vault-disabled-unsafe` key in `config.toml`: ```toml # config.toml (top-level, before any [section] header) hash-vault-disabled-unsafe = true ``` -This runs the node **without any app-hash equivocation protection** and logs error-level warnings on every startup. The default is `false`, and you should re-enable the guard (set it back to `false` or remove the line) as soon as the underlying issue is resolved. +The node then runs without equivocation protection and logs errors at every start. Set the key back to `false` as soon as you fix the problem. diff --git a/snippets/rpc-methods-viewer.jsx b/snippets/rpc-methods-viewer.jsx index 1e1a461..3f896fc 100644 --- a/snippets/rpc-methods-viewer.jsx +++ b/snippets/rpc-methods-viewer.jsx @@ -29,7 +29,7 @@ const NAMESPACE_META = { txpool: { label: 'txpool', color: '#3b82f6', blurb: 'Transaction-pool inspection.' }, net: { label: 'net', color: '#0d9488', blurb: 'Network metadata.' }, web3: { label: 'web3', color: '#7c3aed', blurb: 'Client metadata.' }, - sei: { label: 'sei', color: '#600014', colorDark: '#b99ba1', blurb: 'Remaining legacy Sei extensions for address resolution, cross-VM lookup, and synthetic receipts.', deprecated: true }, + sei: { label: 'sei', color: '#600014', colorDark: '#b99ba1', blurb: 'Remaining legacy Sei extensions for address resolution and cross-VM lookup.', deprecated: true }, admin: { label: 'admin', color: '#6b7280', blurb: 'Node administration is not exposed on Sei.', unavailable: true }, miner: { label: 'miner', color: '#6b7280', blurb: 'Mining control is not applicable (no proof-of-work).', unavailable: true }, clique: { label: 'clique', color: '#6b7280', blurb: 'Clique proof-of-authority is not applicable (CometBFT consensus).', unavailable: true }, @@ -73,7 +73,7 @@ const SEI_RPC_METHODS = [ {"namespace":"eth","name":"eth_getBalance","status":"supported","description":"Returns the wei balance of an account at a given block.","seiNote":"The balance reflects the account's SEI bank balance (18-decimal wei representation). Both EVM and non-EVM (Cosmos bank send or wasm) transactions can change it. The watermark manager resolves the height with a state-version guard.","params":[{"name":"address","type":"DATA, 20 bytes","description":"Account address.","example":"0xDa52B9E673d1f48FcD9916b3F606A136a8eA5e55"},{"name":"blockNrOrHash","type":"BLOCKNUMBER or DATA","description":"Block number, tag (latest/earliest/pending/safe/finalized), or hash.","example":"latest"}]}, {"namespace":"eth","name":"eth_getCode","status":"supported","description":"Returns the contract bytecode at an address for a given block.","params":[{"name":"address","type":"DATA, 20 bytes","description":"Contract address.","example":"0xE30feDd158A2e3b13e9badaeABaFc5516e95e8C7"},{"name":"blockNrOrHash","type":"BLOCKNUMBER or DATA","description":"Block number, tag, or hash.","example":"latest"}]}, {"namespace":"eth","name":"eth_getStorageAt","status":"supported","description":"Returns the value stored at a storage slot of an address at a given block.","seiNote":"Reads the EVM keeper's slot value directly rather than from an MPT trie. The slot key must decode to at most 32 bytes.","params":[{"name":"address","type":"DATA, 20 bytes","description":"Contract address.","example":"0xE30feDd158A2e3b13e9badaeABaFc5516e95e8C7"},{"name":"key","type":"DATA, 32 bytes","description":"Storage slot key (hex, up to 32 bytes).","example":"0x0"},{"name":"blockNrOrHash","type":"BLOCKNUMBER or DATA","description":"Block number, tag, or hash.","example":"latest"}]}, - {"namespace":"eth","name":"eth_getProof","status":"limited","description":"Returns a Merkle proof for an account and the requested storage slots.","seiNote":"Sei stores state in an IAVL tree, not an Ethereum Merkle-Patricia trie. The result is a Sei-specific ProofResult{address, hexValues, storageProof}. Its storageProof entries are CometBFT/IAVL crypto.ProofOps, NOT eth-style MPT proof nodes. There is no accountProof, balance, codeHash, nonce, or storageHash field (Sei has no per-account state root). Standard eth_getProof verifiers do not work.","params":[{"name":"address","type":"DATA, 20 bytes","description":"Account address.","example":"0xE30feDd158A2e3b13e9badaeABaFc5516e95e8C7"},{"name":"storageKeys","type":"array of DATA, 32 bytes","description":"Storage slot keys to prove (bounded by MaxStorageKeysPerProof).","example":"[\"0x0\"]"},{"name":"blockNrOrHash","type":"BLOCKNUMBER or DATA","description":"Block number, tag, or hash.","example":"latest"}]}, + {"namespace":"eth","name":"eth_getProof","status":"unsupported","description":"Returns a Merkle proof for an account and the requested storage slots. Sei registers this method, but it always returns an unsupported error.","seiNote":"Since v6.7.0, always returns JSON-RPC error -32000 with the message 'eth_getProof is not supported yet; please reach out to the Sei Labs if you need this endpoint'."}, {"namespace":"eth","name":"eth_getNonce","status":"limited","description":"Sei-specific helper that returns the current EVM nonce for an address (latest state only).","seiNote":"This non-standard Sei extension is exposed as eth_getNonce and implemented as StateAPI.GetNonce in state.go (NOT on TransactionAPI). Unlike eth_getTransactionCount, it takes no block tag (latest only, through ctxProvider(LatestCtxHeight)) and returns a bare uint64. Prefer eth_getTransactionCount for standard nonce queries.","params":[{"name":"address","type":"DATA, 20 bytes","description":"Account address.","example":"0xDa52B9E673d1f48FcD9916b3F606A136a8eA5e55"}]}, {"namespace":"eth","name":"eth_call","status":"supported","description":"Executes a read-only message call against state without creating a transaction. Supports state and block overrides.","seiNote":"RPCGasCap caps gas, and RPCEVMTimeout caps execution time. A fail-fast limiter may reject the call with 'eth_call rejected due to rate limit: server busy'. Canonical EVM<->Sei address resolution uses eth_call to the addr precompile at 0x0000000000000000000000000000000000001004.","params":[{"name":"args","type":"object","description":"Call object (to, from, data/input, gas, gasPrice/maxFeePerGas, value).","example":"{\"to\":\"0x0000000000000000000000000000000000001004\",\"data\":\"0x0c3c20ed000000000000000000000000Da52B9E673d1f48FcD9916b3F606A136a8eA5e55\"}"},{"name":"blockNrOrHash","type":"BLOCKNUMBER or DATA","description":"Block number, tag, or hash. Defaults to latest.","example":"latest"},{"name":"overrides","type":"object","description":"Optional per-account state overrides (balance, code, nonce, state).","example":"{}"},{"name":"blockOverrides","type":"object","description":"Optional block-context overrides (number, time, coinbase).","example":"{}"}]}, {"namespace":"eth","name":"eth_estimateGas","status":"supported","description":"Estimates the gas needed to execute a transaction.","seiNote":"RPCGasCap bounds this method, and a fail-fast limiter protects it. The block gas limit on Sei is 12.5M. Parallel execution can cause estimates to vary slightly, so size gasLimit with a modest buffer.","params":[{"name":"args","type":"object","description":"Transaction call object.","example":"{\"to\":\"0xE30feDd158A2e3b13e9badaeABaFc5516e95e8C7\",\"data\":\"0x18160ddd\"}"},{"name":"blockNrOrHash","type":"BLOCKNUMBER or DATA","description":"Optional block number, tag, or hash. Defaults to latest.","example":"latest"},{"name":"overrides","type":"object","description":"Optional state overrides.","example":"{}"}]}, @@ -141,7 +141,6 @@ const SEI_RPC_METHODS = [ {"namespace":"sei","name":"sei_getSeiAddress","status":"limited","description":"Returns the native Sei bech32 (sei1...) address associated with a given EVM (0x) address.","seiNote":"This legacy sei_* method is deprecated but ENABLED by default in the enabled_legacy_sei_apis allowlist on seid init. New integrations should call the addr precompile at 0x0000000000000000000000000000000000001004 (getSeiAddr) with eth_call. Returns an error if the address is not yet associated. Works over HTTP only.","params":[{"name":"ethAddress","type":"DATA, 20 bytes","description":"The EVM address to resolve.","example":"0xDa52B9E673d1f48FcD9916b3F606A136a8eA5e55"}]}, {"namespace":"sei","name":"sei_getEVMAddress","status":"limited","description":"Returns the EVM (0x) address associated with a given native Sei bech32 (sei1...) address.","seiNote":"This legacy sei_* method is deprecated but ENABLED by default in enabled_legacy_sei_apis. Prefer the addr precompile at 0x0000000000000000000000000000000000001004 (getEvmAddr) with eth_call. Returns an error if the bech32 is malformed or the address is not yet associated. Works over HTTP only.","params":[{"name":"seiAddress","type":"string","description":"The Sei bech32 address to resolve.","example":"sei13ytysxs88z0fp9cssagg77ekpecrrlrwce9pwl"}]}, {"namespace":"sei","name":"sei_getCosmosTx","status":"limited","description":"Returns the underlying CometBFT/Cosmos transaction hash corresponding to a given EVM transaction hash.","seiNote":"This legacy sei_* method is deprecated but ENABLED by default in enabled_legacy_sei_apis (it has no precompile equivalent yet). Returns the wrapping Cosmos transaction hash as uppercase hexadecimal without a 0x prefix. Works over HTTP only.","params":[{"name":"ethHash","type":"DATA, 32 bytes","description":"The EVM transaction hash to map to its underlying Cosmos tx hash.","example":"0x828c91592453fe7c5bf743204495a35bf02b67b579b8f59ee7eea8af031d7c14"}]}, - {"namespace":"sei","name":"sei_getTransactionReceipt","status":"limited","description":"Returns a transaction receipt by hash, including synthetic receipts for Cosmos transactions.","seiNote":"This legacy sei_* method is deprecated and DISABLED by default. It can get logs for a synthetic transaction when its hash is already known. Works over HTTP only.","params":[{"name":"hash","type":"DATA, 32 bytes","description":"Transaction hash.","example":"0x828c91592453fe7c5bf743204495a35bf02b67b579b8f59ee7eea8af031d7c14"}]}, {"namespace":"admin","name":"admin_*","status":"unsupported","description":"Geth node administration namespace (admin_nodeInfo, admin_peers, admin_addPeer, admin_datadir, and others). Sei does not run this namespace.","seiNote":"Sei does not run go-ethereum's admin namespace. Node administration goes through the CometBFT/Cosmos stack. All admin_* methods return -32601 method not found."}, {"namespace":"miner","name":"miner_*","status":"unsupported","description":"Geth miner-control namespace (miner_start, miner_stop, miner_setEtherbase, miner_setGasPrice, and others). Sei does not run this namespace.","seiNote":"Sei has no PoW or PoA miner (CometBFT PoS consensus) and does not serve the namespace. All miner_* methods return -32601."}, {"namespace":"clique","name":"clique_*","status":"unsupported","description":"Clique proof-of-authority consensus namespace (clique_getSnapshot, clique_propose, and others). Sei does not run this namespace.","seiNote":"Sei uses CometBFT, not the Clique PoA engine, and does not serve the namespace. All clique_* methods return -32601."}, From c844bcdb153f2011488245524f25e271dab3aa50 Mon Sep 17 00:00:00 2001 From: alexander-sei Date: Thu, 15 Oct 2026 14:21:54 +0200 Subject: [PATCH 43/43] docs: correct v6.7 RPC, precompile, and node details Align claims with sei-chain v6.7.1. EVM: - Params returns an empty string for an unknown key instead of reverting - P256 and JSON fees come on top of the calldata charge - Autobahn fullnodes forward pending nonce queries to the shard owner; validators forward transactions there (enable_evm_proxy) - newPendingTransactions and eth_newPendingTransactionFilter are not supported; the pending nonce includes mempool transactions - Fix the @sei-js note, log filter block range, legacy sei_* removal timing, Bank/Authz/Slashing revert conditions, and muxTracer error - Point Governance, Distribution, Staking, and P256 ABI links to v6.7.1 Node: - Fix the hash logger path and file size default, auto write mode and sc-write-mode handling, evm store proofs during migration, littidx receipt backend, lattice hash output, BlockDB flags, and Autobahn and rejected-request metrics Co-authored-by: Cursor --- _typos.toml | 3 ++ evm/evm-parity/evm-compatibility.mdx | 8 ++-- evm/evm-parity/gas-and-fees.mdx | 6 +-- evm/evm-parity/websocket.mdx | 2 +- evm/precompiles/authz.mdx | 1 + evm/precompiles/cosmwasm-precompiles/bank.mdx | 4 +- evm/precompiles/distribution.mdx | 2 +- evm/precompiles/example-usage.mdx | 2 +- evm/precompiles/governance.mdx | 2 +- evm/precompiles/json.mdx | 2 +- evm/precompiles/p256-precompile.mdx | 8 ++-- evm/precompiles/params.mdx | 2 +- evm/precompiles/slashing.mdx | 2 +- evm/precompiles/staking.mdx | 2 +- evm/reference.mdx | 14 +++---- evm/tracing/troubleshooting.mdx | 2 +- node/advanced-config-monitoring.mdx | 16 ++++---- node/giga-storage-migration.mdx | 37 +++++++++++-------- node/node-operators.mdx | 10 +++-- node/technical-reference.mdx | 6 +-- node/troubleshooting.mdx | 2 +- 21 files changed, 72 insertions(+), 61 deletions(-) diff --git a/_typos.toml b/_typos.toml index 07810f2..b49e0ee 100644 --- a/_typos.toml +++ b/_typos.toml @@ -42,3 +42,6 @@ maxiumum = "maxiumum" # "correcting" the snapshot would only diverge from the source of truth). numnber = "numnber" # PR #237 title: "Add next-account-numnber cli" tunning = "tunning" # changelog entry: "log tunning for p2p" +# sei-chain metric label value evmrpc_requests_rejected_total{reason="unparseable"}, +# quoted verbatim in node/advanced-config-monitoring.mdx so PromQL matches. +unparseable = "unparseable" diff --git a/evm/evm-parity/evm-compatibility.mdx b/evm/evm-parity/evm-compatibility.mdx index cc9947f..5e31579 100644 --- a/evm/evm-parity/evm-compatibility.mdx +++ b/evm/evm-parity/evm-compatibility.mdx @@ -119,10 +119,10 @@ These JSON-RPC methods are not available on Sei or return errors: | Method | Behavior on Sei | | --- | --- | -| `eth_getBlockByNumber("pending")` | Returns `null` or the latest committed block. Sei does not maintain an Ethereum-style pending block. | -| `eth_getTransactionCount(address, "pending")` | Returns the same value as `"latest"`. On Sei, the pending nonce is the same as the confirmed nonce. | -| `eth_newPendingTransactionFilter` | You can create the filter, but it does not reliably emit pending transactions. Use `eth_newBlockFilter` or WebSocket block subscriptions instead. | -| `eth_subscribe("newPendingTransactions")` | Supported at the RPC level, but Sei does not guarantee Ethereum-style pending transaction visibility. Treat the results as best effort. | +| `eth_getBlockByNumber("pending")` | Returns the latest committed block. Sei does not maintain an Ethereum-style pending block. | +| `eth_getTransactionCount(address, "pending")` | Returns the sender's next nonce, including the sender's transactions in the mempool. If the sender has no transactions in the mempool, it returns the same value as `"latest"`. | +| `eth_newPendingTransactionFilter` | Not supported. The call always returns an error. Use `eth_newBlockFilter` or a `newHeads` subscription instead. | +| `eth_subscribe("newPendingTransactions")` | Not supported. Sei supports only the `newHeads` and `logs` subscriptions. | ### Proof verification diff --git a/evm/evm-parity/gas-and-fees.mdx b/evm/evm-parity/gas-and-fees.mdx index 5367c6f..ae89c51 100644 --- a/evm/evm-parity/gas-and-fees.mdx +++ b/evm/evm-parity/gas-and-fees.mdx @@ -97,9 +97,9 @@ const gas = await provider.estimateGas({ Sei precompiles charge gas based on the work that each call does: - Before a precompile decodes its arguments, it charges gas for the calldata. The charge grows with the calldata length and with the size of any `string` arguments. A call that cannot pay this charge reverts before the precompile runs. -- The JSON precompile charges 100 gas per byte of JSON input. -- The pointer view precompile charges for the state that each lookup reads. -- The P256 precompile charges a fixed 3,450 gas per verification, the same as RIP-7212 `P256VERIFY`. If the call cannot pay it, the call fails with an out-of-gas error. +- The JSON precompile also charges 100 gas per byte of JSON input. +- The pointer view precompile also charges for the state that each lookup reads. +- The P256 precompile also charges a fixed 3,450 gas per verification, the same as RIP-7212 `P256VERIFY`. If the call cannot pay it, the call fails with an out-of-gas error. Because the cost depends on your calldata, do not hard-code gas limits for precompile calls. Use `eth_estimateGas`, as shown in the example above. diff --git a/evm/evm-parity/websocket.mdx b/evm/evm-parity/websocket.mdx index e33177b..9795ae7 100644 --- a/evm/evm-parity/websocket.mdx +++ b/evm/evm-parity/websocket.mdx @@ -109,7 +109,7 @@ const unwatch = client.watchEvent({ ## Notes - Sei has instant finality, so every block emitted over WebSocket is already final. You do not need to wait for more confirmations before you act on an event. -- Pending transaction subscriptions (`newPendingTransactions`) are supported at the RPC level, but Sei does not guarantee Ethereum-style pending state visibility. +- Sei does not support `newPendingTransactions` subscriptions. You can subscribe only to `newHeads` and `logs`. ## Frame size and concurrency limits diff --git a/evm/precompiles/authz.mdx b/evm/precompiles/authz.mdx index 52e5ba3..02311d9 100644 --- a/evm/precompiles/authz.mdx +++ b/evm/precompiles/authz.mdx @@ -71,6 +71,7 @@ interface IAuthz { ## Notes - Every address argument must have an associated Sei address. Otherwise, the call reverts. +- If you pass a `msgTypeUrl` and no grant matches it, `grants` reverts. - `authorization` is the grant's authorization encoded as JSON. It includes an `@type` field that names the authorization type. - `expiration` is a Unix timestamp in seconds. In `GrantAuthorization`, `granter` and `grantee` are Sei addresses. - For paginated methods, pass empty bytes for the first page, then pass the returned `nextKey`. An empty `nextKey` means there are no more pages. diff --git a/evm/precompiles/cosmwasm-precompiles/bank.mdx b/evm/precompiles/cosmwasm-precompiles/bank.mdx index 3f4e840..d73913c 100644 --- a/evm/precompiles/cosmwasm-precompiles/bank.mdx +++ b/evm/precompiles/cosmwasm-precompiles/bank.mdx @@ -139,7 +139,7 @@ Validate the recipient before you send SEI. A successful transaction cannot be r ## Other query methods -Since v6.7.0, the Bank precompile also has read-only queries for spendable balances, total supply, Bank Module parameters, and denomination metadata. +The Bank precompile also has the read-only queries below. All of them except `supply` are available since v6.7.0. ```solidity function supply(string memory denom) external view returns (uint256 response); @@ -183,7 +183,7 @@ struct Metadata { } ``` -`supply(denom)` returns the supply of one denomination. `totalSupply(pageKey)` returns the supply of every denomination, one page at a time. For paginated methods, pass empty bytes (`0x`) for the first page, then pass the returned `nextKey`. An empty `nextKey` means there are no more pages. +`supply(denom)` returns the supply of one denomination. `totalSupply(pageKey)` returns the supply of every denomination, one page at a time. For paginated methods, pass empty bytes (`0x`) for the first page, then pass the returned `nextKey`. An empty `nextKey` means there are no more pages. `spendableBalances` reverts if `acc` has no associated Sei address. `BANK_PRECOMPILE_ABI` in `@sei-js/precompiles@3` does not include `spendableBalances`, `totalSupply`, `params`, `denomMetadata`, or `denomsMetadata`. Call them with an ABI fragment: diff --git a/evm/precompiles/distribution.mdx b/evm/precompiles/distribution.mdx index 33f3a1c..5678f77 100644 --- a/evm/precompiles/distribution.mdx +++ b/evm/precompiles/distribution.mdx @@ -488,4 +488,4 @@ function setupTreasuryWithdrawals(address treasury) external onlyOwner { } ``` -View the complete distribution precompile ABI at the [Sei Chain v6.6.1 snapshot](https://github.com/sei-protocol/sei-chain/blob/v6.6.1/precompiles/distribution/legacy/v66/abi.json). +View the complete distribution precompile ABI in [sei-chain v6.7.1](https://github.com/sei-protocol/sei-chain/blob/v6.7.1/precompiles/distribution/abi.json). diff --git a/evm/precompiles/example-usage.mdx b/evm/precompiles/example-usage.mdx index d6f7a9b..a2929d6 100644 --- a/evm/precompiles/example-usage.mdx +++ b/evm/precompiles/example-usage.mdx @@ -37,7 +37,7 @@ This page shows Bank, Staking, Governance, Distribution, JSON, and Solo. The oth The `@sei-js/precompiles` package does not export the IBC and Oracle precompiles. IBC is disabled in both directions, and native Oracle queries are retired. Calls to either precompile cannot succeed on live Sei. See [IBC is disabled](/learn/sip-03-migration#ibc-is-disabled) and [Oracle Precompile (Retired)](/evm/precompiles/oracle). -`@sei-js/precompiles@3` is ESM-only and matches the Sei Chain v6.6.1 ABIs. It does not include the Auth, Authz, Evidence, Mint, Params, Slashing, and Upgrade precompiles, or the query methods that v6.7 added to Bank and Governance. For those, compile the Solidity interface on each precompile's page to get the ABI. Import raw `*_PRECOMPILE_ABI` constants from the package root, `@sei-js/precompiles/precompiles`, or `@sei-js/precompiles/viem`. Import the ethers factories from `@sei-js/precompiles/ethers`. The package does not expose `VIEM_*_PRECOMPILE_ABI` aliases. It re-exports the viem `sei` and `seiTestnet` definitions and also exports `seiLocal` (chain ID `713714`, `http://localhost:8545`). The package requires viem 2.55.16 or newer. +`@sei-js/precompiles@3` is ESM-only and matches the Sei Chain v6.6.1 ABIs. It does not include the Auth, Authz, Evidence, Mint, Params, Slashing, and Upgrade precompiles. It also does not include the methods that v6.7 added to Bank, Distribution, Governance, and Staking. For those ABIs, use the `abi.json` files in the [sei-chain `precompiles` directory](https://github.com/sei-protocol/sei-chain/tree/v6.7.1/precompiles). Import raw `*_PRECOMPILE_ABI` constants from the package root, `@sei-js/precompiles/precompiles`, or `@sei-js/precompiles/viem`. Import the ethers factories from `@sei-js/precompiles/ethers`. The package does not expose `VIEM_*_PRECOMPILE_ABI` aliases. It re-exports the viem `sei` and `seiTestnet` definitions and also exports `seiLocal` (chain ID `713714`, `http://localhost:8545`). The package requires viem 2.55.16 or newer. ```ts import { getStakingPrecompileEthersV6Contract } from '@sei-js/precompiles/ethers'; diff --git a/evm/precompiles/governance.mdx b/evm/precompiles/governance.mdx index 259f079..f76a95e 100644 --- a/evm/precompiles/governance.mdx +++ b/evm/precompiles/governance.mdx @@ -240,7 +240,7 @@ npm install dotenv ```typescript // Import Governance precompile address and ABI -// View the entire ABI here: https://github.com/sei-protocol/sei-chain/blob/v6.6.1/precompiles/gov/legacy/v66/abi.json +// View the entire ABI here: https://github.com/sei-protocol/sei-chain/blob/v6.7.1/precompiles/gov/abi.json import { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } from '@sei-js/precompiles'; import { ethers } from 'ethers'; ``` diff --git a/evm/precompiles/json.mdx b/evm/precompiles/json.mdx index 80726f9..05f2d75 100644 --- a/evm/precompiles/json.mdx +++ b/evm/precompiles/json.mdx @@ -713,7 +713,7 @@ async function handleMissingKeys(jsonPrecompile: ethers.Contract, data: any) { - **Boolean values:** Use the integer 0 for false and 1 for true. - **Key paths:** Extract parent objects first, then parse them manually. Dot notation may not be supported. - **Arrays:** Object-keyed arrays use `extractAsBytesList()`. JSON arrays by index use `extractAsBytesFromArray()`. -- **Gas costs:** The precompile charges 100 gas per byte of JSON input. Large JSON inputs need higher gas limits. +- **Gas costs:** The precompile charges 100 gas per byte of JSON input. Like every Sei precompile, it also charges for the calldata. Large JSON inputs need higher gas limits. - **Encoding:** Always use UTF-8 encoding with `ethers.toUtf8Bytes()`. - **Error handling:** Always implement fallback values for production applications. diff --git a/evm/precompiles/p256-precompile.mdx b/evm/precompiles/p256-precompile.mdx index 2f66dd7..b39b8c1 100644 --- a/evm/precompiles/p256-precompile.mdx +++ b/evm/precompiles/p256-precompile.mdx @@ -162,9 +162,9 @@ Total length: 160 bytes ## Gas costs -Each verification costs a fixed 3,450 gas, the same as the RIP-7212 `P256VERIFY` precompile. The cost does not depend on the input size. It is much lower than the cost of P-256 verification in Solidity. +Each verification costs a fixed 3,450 gas, the same as the RIP-7212 `P256VERIFY` precompile. This cost is much lower than the cost of P-256 verification in Solidity. Like every Sei precompile, the call also pays a charge for its calldata. For details, see [Gas for precompile calls](/evm/evm-parity/gas-and-fees#gas-for-precompile-calls). -The precompile charges this gas before it checks the signature. If the call cannot pay this gas, the call fails with an out-of-gas error. The call does not revert. +The precompile charges for the calldata first. Then it charges the 3,450 gas, and then it checks the signature. If the call cannot pay the calldata charge, it reverts. If the call cannot pay the 3,450 gas, it fails with an out-of-gas error and does not revert. ## Real-world use cases @@ -435,9 +435,9 @@ contract P256Test { ## Performance considerations -- **Gas efficiency**: A verification costs a fixed 3,450 gas +- **Gas efficiency**: A verification costs a fixed 3,450 gas, plus the calldata charge - **Batch operations**: Consider batching multiple verifications in a single transaction - **Caching**: Cache public keys on-chain to reduce calldata for repeated verifications - **Hardware integration**: Particularly efficient for applications that use hardware-backed keys -View the complete P256 precompile ABI in the [sei-chain v6.6.1 snapshot](https://github.com/sei-protocol/sei-chain/blob/v6.6.1/precompiles/p256/legacy/v66/abi.json), or browse the [implementation source](https://github.com/sei-protocol/sei-chain/tree/v6.6.1/precompiles/p256). +View the complete P256 precompile ABI in [sei-chain v6.7.1](https://github.com/sei-protocol/sei-chain/blob/v6.7.1/precompiles/p256/abi.json), or browse the [implementation source](https://github.com/sei-protocol/sei-chain/tree/v6.7.1/precompiles/p256). diff --git a/evm/precompiles/params.mdx b/evm/precompiles/params.mdx index ff762f8..6c707bc 100644 --- a/evm/precompiles/params.mdx +++ b/evm/precompiles/params.mdx @@ -29,4 +29,4 @@ interface IParams { | --- | --- | | `params(subspace, key)` | The value of the parameter `key` in `subspace`, as a JSON string. For example, `params("staking", "MaxValidators")` returns the maximum number of validators. | -The call reverts if `subspace` or `key` is empty or unknown. +The call reverts if `subspace` or `key` is empty, or if `subspace` does not exist. If `key` does not exist in `subspace`, the call returns an empty string. diff --git a/evm/precompiles/slashing.mdx b/evm/precompiles/slashing.mdx index 9c1636c..d7aa23d 100644 --- a/evm/precompiles/slashing.mdx +++ b/evm/precompiles/slashing.mdx @@ -73,4 +73,4 @@ interface ISlashing { | `unjailWithAuthorization(validatorOperator)` | Unjails the validator of `validatorOperator`, using a grant that `validatorOperator` gave the caller. | | `revokeUnjailAuthorization(grantee)` | Removes the unjail grant that the caller gave `grantee`. | -Each transaction returns `true` if it succeeds. Transactions do not accept value. A transaction reverts if you call it through `delegatecall` or `staticcall`. The caller and every address argument must have an associated Sei address. `unjail()` also reverts if the caller is not a validator operator, or if the validator does not meet the unjail conditions. +Each transaction returns `true` if it succeeds. Transactions do not accept value. A transaction reverts if you call it through `delegatecall` or `staticcall`. The caller and every address argument must have an associated Sei address. `unjail()` also reverts if the caller is not a validator operator, or if the validator does not meet the unjail conditions. `grantUnjailAuthorization` reverts if `expiration` is not after the current block time. `revokeUnjailAuthorization` reverts if there is no grant to remove. diff --git a/evm/precompiles/staking.mdx b/evm/precompiles/staking.mdx index cb1e7c8..ff85ce3 100644 --- a/evm/precompiles/staking.mdx +++ b/evm/precompiles/staking.mdx @@ -489,7 +489,7 @@ npm install @sei-js/precompiles@^3 ```typescript // Import Staking precompile address and ABI -// View the entire ABI here: https://github.com/sei-protocol/sei-chain/blob/v6.6.1/precompiles/staking/legacy/v66/abi.json +// View the entire ABI here: https://github.com/sei-protocol/sei-chain/blob/v6.7.1/precompiles/staking/abi.json import { STAKING_PRECOMPILE_ABI, STAKING_PRECOMPILE_ADDRESS } from '@sei-js/precompiles'; import { ethers } from 'ethers'; ``` diff --git a/evm/reference.mdx b/evm/reference.mdx index 8134a94..49b295a 100644 --- a/evm/reference.mdx +++ b/evm/reference.mdx @@ -23,7 +23,7 @@ By default, the explorer hides the remaining deprecated `sei_*` extensions and t ## Overview -Sei supports the [Ethereum JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/). It also has some Sei-specific extensions for cross-VM operations, synthetic transactions, and other advanced features. Because Sei has [instant finality](/evm/differences-with-ethereum#finality), the `safe`, `finalized`, and `latest` block tags all resolve to the same (latest committed) block. +Sei supports the [Ethereum JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/). It also has a few deprecated Sei-specific extensions for address resolution and cross-VM lookups. Because Sei has [instant finality](/evm/differences-with-ethereum#finality), the `safe`, `finalized`, and `latest` block tags all resolve to the same (latest committed) block. All endpoints follow the standard JSON-RPC format: @@ -112,9 +112,9 @@ By default, log filters (`eth_getLogs`, `eth_getFilterLogs`) have these limits. - **Matched logs:** a query can match up to 10,000 logs (`max_log_no_block`), for both bounded and open-ended block ranges. - **Log data:** a query can load up to about 64 MiB of matched logs (`max_log_bytes`). -- **Closed block range:** a query can span up to 2,000 blocks (`max_blocks_for_log`). +- **Block range:** a query can span up to 2,000 blocks (`max_blocks_for_log`). This limit also applies to a range that ends at the latest block. -A query over the log count or log data limit fails with `query matches too many logs` or `query matches too many log bytes`. It does not return partial results. Narrow the block range or the filters (address and topics), then try again. +A query over a limit fails with `query matches too many logs`, `query matches too many log bytes`, or `block range too large`. It does not return partial results. Narrow the block range or the filters (address and topics), then try again. Real-time subscriptions (`eth_subscribe` for `newHeads` and `logs`) are available over WebSocket only. For transport details, see [WebSocket connections](/evm/evm-parity/websocket). @@ -132,11 +132,11 @@ You can also browse every method below in the interactive explorer above. This s **Under Autobahn (`AutobahnConfigFile` set):** The node runs as a validator when the top-level `mode` in `config.toml` is `"validator"`. With any other mode, it runs as a fullnode without a local mempool. -On a validator, transactions go to Autobahn's producer mempool instead of CometBFT's `TxMempool`. This mempool admits EVM transactions in strict nonce order for each sender. It rejects a transaction with a bad-nonce error if its nonce is not the sender's next expected nonce, so senders must submit nonces without gaps. +On a validator, transactions go to Autobahn's producer mempool instead of CometBFT's `TxMempool`. This mempool admits EVM transactions in strict nonce order for each sender. It rejects a transaction with a bad-nonce error if its nonce is not the sender's next expected nonce, so senders must submit nonces without gaps. If another validator owns the sender's EVM shard and is connected, the validator forwards the transaction to that validator instead. The `enable_evm_proxy` field in `autobahn.json` controls this forwarding. Its default is `true`. -On a fullnode, `eth_sendRawTransaction` forwards the transaction to the committee member that owns the sender's EVM shard. For this, every validator in `autobahn.json` must have an `evmrpc` URL. The Tendermint `broadcast_tx_async`, `broadcast_tx_sync`, and `broadcast_tx_commit` methods fail on a fullnode with `autobahn fullnode has no local mempool; broadcast_tx_* must be sent to a validator`. On a fullnode, `eth_getTransactionCount` with the `pending` tag returns the confirmed nonce, and `unconfirmed_txs` and `num_unconfirmed_txs` return empty results. +On a fullnode, `eth_sendRawTransaction` forwards the transaction to the committee member that owns the sender's EVM shard. For this, every validator in `autobahn.json` must have an `evmrpc` URL. The Tendermint `broadcast_tx_async`, `broadcast_tx_sync`, and `broadcast_tx_commit` methods fail on a fullnode with `autobahn fullnode has no local mempool; broadcast_tx_* must be sent to a validator`. On a fullnode, `eth_getTransactionCount` with the `pending` tag also goes to the shard owner. If the fullnode has no EVM RPC connection to the shard owner, the call returns `0`. `unconfirmed_txs` and `num_unconfirmed_txs` return empty results on a fullnode. -Under Autobahn, the default CheckTx-synchronous broadcast (`broadcast_tx_sync`, the same path named above) blocks while the mempool is full. It returns only after capacity is available. As a result, a default `eth_sendRawTransaction` call can stall for as long as the mempool stays full. The async path (`broadcast_tx_async`) may silently drop the transaction instead. The `unsafe_flush_mempool` Tendermint RPC endpoint is not supported under Autobahn and returns `unsafe_flush_mempool is not supported with autobahn mempool`. +On an Autobahn validator, the default CheckTx-synchronous broadcast (`broadcast_tx_sync`, the same path named above) blocks while the mempool is full. It returns only after capacity is available. As a result, a default `eth_sendRawTransaction` call can stall for as long as the mempool stays full. The async path (`broadcast_tx_async`) can drop the transaction without an error instead. The `unsafe_flush_mempool` Tendermint RPC endpoint is not supported under Autobahn and returns `unsafe_flush_mempool is not supported with autobahn mempool`. **Parameters:** @@ -1359,7 +1359,7 @@ Sei keeps three legacy custom endpoints for address resolution and cross-VM tran For new integrations, use standard `eth_*` and `debug_*` methods. The three remaining `sei_*` methods are deprecated and scheduled for removal. -The `sei2_*` namespace has been removed. Most legacy `sei_*` methods have also been removed, including block, filter, log, signing, association, and transaction lookup methods. The `sei_*` cleanup is tracked in [sei-chain PR #3927](https://github.com/sei-protocol/sei-chain/pull/3927). Since v6.7.0, `sei_getTransactionReceipt` is also removed. +In v6.7.0, Sei removed the `sei2_*` namespace and most legacy `sei_*` methods. The removed methods include `sei_getTransactionReceipt` and the block, filter, log, signing, association, and transaction lookup methods. The `sei_*` cleanup is tracked in [sei-chain PR #3927](https://github.com/sei-protocol/sei-chain/pull/3927). No RPC method returns receipts or logs for synthetic transactions from Cosmos-originated messages. `eth_getTransactionReceipt` returns receipts for EVM transactions only. diff --git a/evm/tracing/troubleshooting.mdx b/evm/tracing/troubleshooting.mdx index bcd6fb8..0e5f125 100644 --- a/evm/tracing/troubleshooting.mdx +++ b/evm/tracing/troubleshooting.mdx @@ -81,7 +81,7 @@ This guide covers common issues with EVM transaction tracing on Sei and gives pr **Error**: `debug tracer "..." is not allowed; JavaScript tracers are disabled and only native tracers listed in evm.trace_allowed_tracers may be used` -**Cause**: The node accepts only the native tracers in `trace_allowed_tracers` and rejects JavaScript tracers by default. For `muxTracer`, a nested tracer that is not on the list fails with `nested debug tracer "..." is not allowed`. +**Cause**: The node accepts only the native tracers in `trace_allowed_tracers` and rejects JavaScript tracers by default. For `muxTracer`, a nested tracer that is not allowed fails with an `invalid muxTracer config` error. **Solutions**: diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index 7281f02..8ecf662 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -523,11 +523,11 @@ If a seed node cannot open the Prometheus listener, the seed fails to start. For The metrics server uses a 10s read header timeout, a 10s read timeout, a 30s write timeout, and a 60s idle timeout. -`max-open-connections` under `[instrumentation]` is not a connection limit. It caps the number of scrapes that the server handles at the same time. Requests over the cap get HTTP `503`. The server does not queue them. A slow scraper holds its slot until it finishes or reaches the write timeout. `0` means no limit. Set the value higher than the number of scrapers that can overlap, such as an HA Prometheus pair, probes, and manual `curl` requests. +`max-open-connections` under `[instrumentation]` is not a connection limit. It caps the number of scrapes that the server handles at the same time. Requests over the cap get HTTP `503`. The server does not queue them. A slow scraper holds its slot until it finishes or reaches the write timeout. `0` means no limit. The default is `3`. Set the value higher than the number of scrapers that can overlap, such as an HA Prometheus pair, probes, and manual `curl` requests. ## `chain_id` label on metrics -Since v6.7.0, the node adds a `chain_id` label to every metric that it exports. This includes the Tendermint Prometheus metrics, the Cosmos SDK telemetry, and the OpenTelemetry metrics (`evmrpc_*`, `flatkv_*`, `litt_*`, and `pebble_*`). If you already set `chain_id` in `global-labels` under `[telemetry]`, the node keeps your value. +Since v6.7.0, the node adds a `chain_id` label to every metric that it exports. This includes the Tendermint Prometheus metrics, the Cosmos SDK telemetry, and the OpenTelemetry metrics (`evmrpc_*`, `flatkv_*`, `litt_*`, and `pebble_*`). If you already set `chain_id` in `global-labels` under `[telemetry]`, the Cosmos SDK telemetry keeps your value. Update PromQL that aggregates across series, such as `sum(...) without (...)` or `by (...)`, to account for the new label. To scope a query to one chain, add a `chain_id` match. @@ -537,8 +537,8 @@ The `tendermint` namespace also has these subsystems: | Subsystem prefix | Description | | --- | --- | -| `tendermint_internal_autobahn_avail_*` | Autobahn availability metrics: the commit and app road index, the commit and app global block number, the proposal-to-commit latency, and the commit-to-commit latency (with a `timeouts` label). | -| `tendermint_internal_autobahn_data_*` | A `latency` histogram for Autobahn data, with `resource` and `stage` labels. It measures the time from the production of a resource to each stage. | +| `tendermint_internal_autobahn_avail_*` | Autobahn availability metrics: the commit road index, the commit global block number, the proposal-to-commit latency, and the commit-to-commit latency (with a `timeouts` label). | +| `tendermint_internal_autobahn_data_*` | Autobahn data metrics: `latency` (time from the production of a resource to each stage, with `resource` and `stage` labels), `next_block` (the next block to process in each stage), `gas_used` (gas used by executed blocks), and `tx_size` (size of executed transactions in bytes). | | `tendermint_internal_p2p_mux_*` | p2p mux stream metrics: the stream `latency`, the number of open streams (`in_flight`), and the per-stream counters `send_msgs`, `recv_msgs`, `send_bytes`, and `recv_bytes`. These metrics have `role` and `kind` labels. | ## EVM RPC OpenTelemetry metrics @@ -553,7 +553,7 @@ The EVM RPC layer emits OpenTelemetry metrics through the process-wide `MeterPro | `evmrpc_websocket_connects_total` | Counter | Number of new WebSocket connections. | | `evmrpc_redirected_requests_total` | Counter | Number of EVM RPC requests forwarded to another validator. Labeled by `endpoint` and `connection`. | | `evmrpc_historical_debug_trace_attempts_total` | Counter | Number of `debug_trace*` requests that target historical blocks beyond the configured max block lookback. Labeled by `endpoint` and `connection`. | -| `evmrpc_requests_rejected_total` | Counter | Number of JSON-RPC requests that admission control rejected. Labeled by `protocol` (`http` or `ws`) and `reason`. `oversize` means the request was larger than `max_request_body_bytes` (HTTP 413, or WebSocket close code `1009`). `busy` means the `max_concurrent_request_bytes` budget was full (HTTP 429, or JSON-RPC error `-32005` after `ws_admission_timeout` on WebSocket). See [Frame size and concurrency limits](/evm/evm-parity/websocket#frame-size-and-concurrency-limits). | +| `evmrpc_requests_rejected_total` | Counter | Number of JSON-RPC requests that admission control rejected. Labeled by `protocol` (`http` or `ws`) and `reason`. For HTTP, `reason` is `oversize` (larger than `max_request_body_bytes`, HTTP 413), `budget_midread` (the `max_concurrent_request_bytes` budget ran out, HTTP 429), `slow_body`, `rate_limited`, `unparseable`, or `read_error`. For WebSocket, `reason` is `oversize` (close code `1009`) or `busy` (no budget within `ws_admission_timeout`, JSON-RPC error `-32005`). See [Frame size and concurrency limits](/evm/evm-parity/websocket#frame-size-and-concurrency-limits). | The `evmrpc_request_latency_seconds` histogram carries these labels: @@ -654,14 +654,14 @@ log-filter-parallelism = 16 The state-commit store writes a CSV file of named hashes for each block. Each row has the memIAVL module and root hashes and the FlatKV DB and root hashes. It also has the app hash, the block hash, the results hash (the next block's `LastResultsHash`), and the changeset hash. To find where two nodes diverged, compare their logs with [`seidb hashlog compare`](/node/technical-reference#inspecting-hash-log-archives). -The hash logger is on by default and writes to `/data/hash.log`. With the default settings, it keeps up to 16 GiB of closed log files. Configure it in the `[state-commit]` section of `app.toml`: +The hash logger is on by default. It writes to `data/hash.log` under the state-commit directory, which is `/data/hash.log` by default. With the default settings, it keeps up to 16 GiB of closed log files. Configure it in the `[state-commit]` section of `app.toml`: | Config key | Default | Description | | --- | --- | --- | | `sc-hash-logger-enable` | `true` | Turns the hash logger on or off. | -| `sc-hash-logger-directory` | empty | Directory for the log files. Empty means `/data/hash.log`. | +| `sc-hash-logger-directory` | empty | Directory for the log files. Empty means `data/hash.log` under the state-commit directory. | | `sc-hash-logger-blocks-to-retain` | `0` | Number of recent blocks to keep. `0` turns off the block limit. | -| `sc-hash-logger-target-file-size` | `16777216` (16 MiB) | Size in bytes at which the logger closes a file and starts a new one. Must be greater than `0`. | +| `sc-hash-logger-target-file-size` | `16777216` (16 MiB) | Size in bytes at which the logger closes a file and starts a new one. `0` uses the default. | | `sc-hash-logger-max-disk-size` | `17179869184` (16 GiB) | Maximum total size in bytes of closed log files. `0` turns off the size limit. | If you set both `sc-hash-logger-blocks-to-retain` and `sc-hash-logger-max-disk-size` to `0`, the logs grow without limit. To turn the hash logger off: diff --git a/node/giga-storage-migration.mdx b/node/giga-storage-migration.mdx index 75cfce4..06e5e39 100644 --- a/node/giga-storage-migration.mdx +++ b/node/giga-storage-migration.mdx @@ -17,9 +17,8 @@ After the migration, the SS layer is repartitioned into two cooperating stores: | **SC** (State Commit, app hash) | `memiavl` | FlatKV | | **SS** (State Store, historical queries) | single PebbleDB MVCC database | dedicated EVM SS MVCC databases in the configured EVM SS directory | -This migration changes only the **SS** layer. The SC layer config stays the same, -and `memiavl` remains the authoritative source for the app hash, so the change is -invisible to the network. +This migration changes only the **SS** layer. It does not change the SC layer or +the app hash, so the change is invisible to the network. This guide follows the canonical procedure in the `sei-chain` repository: [`docs/migration/giga_store_migration.md`](https://github.com/sei-protocol/sei-chain/blob/main/docs/migration/giga_store_migration.md). If anything here drifts, open an issue there. @@ -209,8 +208,9 @@ consensus halts. Since v6.7.0, governance controls the migration, and nodes follow it without config changes: -- By default, every node runs in the `auto` write mode. The node ignores - `sc-write-mode` and derives its mode from the migration state on disk. +- By default, every node runs in the `auto` write mode. The node derives its + mode from the migration state on disk. It checks that `sc-write-mode` has a + valid value, but it does not use the value. - The `NumKeysToMigratePerBlock` parameter in the `migration` params subspace sets how many keys move per block. The default, `0`, keeps the migration paused. A `ParameterChangeProposal` can raise it to at most 1,000,000. @@ -222,11 +222,12 @@ In v6.7, governance can start only the EVM migration. The later write modes (`migrate_all_but_bank`, `all_migrated_but_bank`, `migrate_bank`, and `flatkv_only`) exist, but no parameter starts them. -During the migration, the node reads and iterates over both backends. Each key -is in one backend at a time, because the migration deletes a key from memiavl -when it copies the key to FlatKV. The migration boundary advances at most once -per block. FlatKV has no proof builder, so state proofs work only for data that -is still in memiavl. +During the migration, the node reads and iterates over both backends. The +migration deletes a key from memiavl when it copies the key to FlatKV. If both +backends report a key, the node uses the memiavl value. The migration boundary +advances at most once per block. When the migration starts, the node stops +serving state proofs for the `evm` store, because FlatKV has no proof builder. +Proofs for other modules still work. ### Pinning a write mode @@ -249,7 +250,7 @@ A node pinned to `memiavl_only` does not take part in the migration. When governance starts the migration, the pinned node diverges from the network. Since v6.7.1, a node also refuses to start if a pinned `evm_migrated`, `all_migrated_but_bank`, or `flatkv_only` mode disagrees with the migration -state on disk. The `cosmos_only` value from v6.4 and v6.5 `app.toml` files is +state on disk. The error starts with `write mode is pinned to`. The `cosmos_only` value from v6.4 and v6.5 `app.toml` files is still accepted as an alias for `memiavl_only`. ### Write modes @@ -265,8 +266,9 @@ still accepted as an alias for `memiavl_only`. | `flatkv_only` | FlatKV is the only SC backend. memiavl is not created. A node can state sync directly into this mode. | | `auto` | Derives the mode from the migration state on disk. This is the default. | -For the local Docker devnet, `GIGA_MIGRATE_FROM_MEMIAVL=true` starts every node -in `memiavl_only`, where the migration begins. Do not set it together with +For the local Docker devnet, `GIGA_MIGRATE_FROM_MEMIAVL=true` pins every node to +`memiavl_only` (with `sc-write-mode-enable-auto = false`), the starting point of +the devnet migration test. Do not set it together with `GIGA_STORAGE`. If you set both, `GIGA_MIGRATE_FROM_MEMIAVL` takes precedence. ```bash copy @@ -302,6 +304,10 @@ The `seidb import-flatkv-from-memiavl` command populates a FlatKV store from an - The import height must equal the latest memIAVL version. The command refuses a lower height, because the composite store's version reconciliation would silently roll memIAVL back and truncate blocks. It also refuses a higher height. First, check the current version with `seidb memiavl-latest-version`. If needed, roll memIAVL back to the target height before you import. - Overwriting existing committed FlatKV data requires the explicit `--force` flag. +In the default `auto` write mode, the node does not use a FlatKV store that this +command creates. The store has no migration metadata, so the node starts in +`memiavl_only`. + ## FAQ ### Where do the data files live after migrating? @@ -319,9 +325,8 @@ The `seidb import-flatkv-from-memiavl` command populates a FlatKV store from an ### Does Giga SS Store change the app hash or consensus? -No. The SC layer is unchanged, so `memiavl` remains the authoritative source -for the app hash. Giga SS Store is a per-node SS change that is invisible to -the network. +No. Giga SS Store changes only the SS layer, which does not affect the app hash. +It is a per-node change that is invisible to the network. ### Can I migrate a validator node with this guide? diff --git a/node/node-operators.mdx b/node/node-operators.mdx index bd248f9..22104c5 100644 --- a/node/node-operators.mdx +++ b/node/node-operators.mdx @@ -1686,11 +1686,13 @@ sc-snapshot-min-time-interval = 3600 # Cap on snapshot write throughput across all trees (MB/s). 0 = unlimited. sc-snapshot-write-rate-mbps = 100 -# The node runs in the auto write mode by default and ignores sc-write-mode. +# The node runs in the auto write mode by default. It checks that sc-write-mode +# has a valid value, but it does not use the value. # To pin a mode, also set sc-write-mode-enable-auto = false. Read the Giga # storage migration guide first. -# sc-read-mode, sc-enable-lattice-hash, and sc-keys-to-migrate-per-block are -# not seid config keys. The node ignores them, so you can remove them. +# sc-read-mode and sc-enable-lattice-hash are not seid config keys. Since +# v6.7.0, the node also ignores sc-keys-to-migrate-per-block. You can remove +# these keys. sc-write-mode = "memiavl_only" [state-commit.flatkv] @@ -1719,7 +1721,7 @@ evm-ss-split = false evm-ss-separate-dbs = false [receipt-store] -# pebbledb is the only supported backend (parquet was removed in v6.6.0). +# Supported backends: pebbledb (aka pebble) and littidx. parquet was removed in v6.6.0. # Receipts are pruned together with blocks according to min-retain-blocks above. rs-backend = "pebbledb" async-write-buffer = 100 diff --git a/node/technical-reference.mdx b/node/technical-reference.mdx index 41688a7..a378f79 100644 --- a/node/technical-reference.mdx +++ b/node/technical-reference.mdx @@ -243,7 +243,7 @@ The command accepts these flags: - `--lthash-only`: Check the LtHash, but do not write dump files. It requires `--lthash=true`. Do not use it with `--bucket`. - `--read-limit-mb`: The maximum read speed in MiB/s. The default is `64`, and `0` turns the limit off. Raise it only for offline runs. -With `--lthash`, the output lists the count and checksum of each bucket, the `TOTAL`, and a `PASS` or `FAIL` line. On `FAIL`, the command exits with a non-zero code. The command skips the check when the snapshot has no committed LtHash to compare with. +With `--lthash`, the output lists the count and checksum of each bucket, the `TOTAL`, and a `PASS` or `FAIL` line. On a mismatch, the command prints `TOTAL FAIL` and stops with an error. The command skips the check when the snapshot has no committed LtHash to compare with. #### Inspecting hash log archives @@ -291,9 +291,9 @@ The `--persistent-state-dir` flag controls where Autobahn persists its consensus Autobahn stores blocks and commit quorum certificates in a LittDB-based BlockDB under `/blockdb`. Since v6.7.0, it no longer reads the old `globalblocks/` and `fullcommitqcs/` write-ahead log directories. -Two more flags tune the BlockDB. When you set either flag, the command writes a `block_db` section into `autobahn.json`: +Two more flags tune the BlockDB. The command writes their values to the `block_db` section of `autobahn.json`: -- `--blockdb-retention`: How long the BlockDB keeps data. The default is `30s`, because this command targets local and Docker clusters. Pass an empty value (`--blockdb-retention=`) to omit the field and use the BlockDB default of 24 hours. +- `--blockdb-retention`: The minimum age before the BlockDB can delete pruned records. The default is `30s`, because this command targets local and Docker clusters. Pass an empty value (`--blockdb-retention=`) to omit the field and use the BlockDB default of 1 hour. - `--blockdb-gc-period`: How often the BlockDB runs garbage collection, for example `10s`. If you omit it, the BlockDB default applies. ```json diff --git a/node/troubleshooting.mdx b/node/troubleshooting.mdx index 45327b0..69ef919 100644 --- a/node/troubleshooting.mdx +++ b/node/troubleshooting.mdx @@ -125,7 +125,7 @@ To check only the FlatKV lattice hash against the snapshot metadata, with no dum seidb dump-flatkv --db-dir $HOME/.sei/data/state_commit/flatkv --lthash-only ``` -The output ends with a `PASS` or `FAIL` line for the lattice hash check. On `FAIL`, the command exits with a non-zero code. +If the lattice hash matches, the output shows `result: PASS`. On a mismatch, the command prints `TOTAL FAIL` and stops with an error. ### Comparing EVM state between memIAVL and FlatKV