docs: v6.6.0 catch-up — verified subset - #59
Conversation
Five additions from the v6.6.0 catch-up, each verified against sei-chain at tag v6.6.0 rather than against the PR description that proposed it. - state-proofs: eth_getProof now rejects non-hex storage keys and caps a request at 1024 keys. Verified in #3556: `const MaxStorageKeysPerProof = 1024` and the errors "too many storage keys: got %d, max %d" and "invalid storage key %q" verbatim. - transaction-types: EIP-7702 SetCode requires a non-empty auth list. The error string "auth list cannot be empty" is in x/evm/types/ethtx/semantic_validation.go. - evm-compatibility: eth_getProof resolves across store backends, not only classic IAVL. - gas-and-fees: receipts report the actual effectiveGasPrice for type-2 transactions, min(baseFee + tip, maxFee), matching EIP-1559 semantics. - websocket: newHeads under Autobahn returns zero hashes for parentHash, receiptsRoot and transactionsRoot, sources stateRoot from AppHash, and approximates gasUsed — so head-chaining subscribers need another mechanism.
- precompiles/json: extractAsUint256 rejects value strings over 100 characters. Verified at precompiles/json/json.go:175 — `if len(strValue) > 100`. - transactions: replace the placeholder effectiveGasPrice "0x1234" with a realistic value. Cosmetic, no behavioural claim.
- rocksdb-backend: fresh PebbleDB state stores use a descending-version MVCC encoding so latest-version reads land on the newest version directly. Verified at sei-db/db_engine/pebbledb/mvcc/db.go:44 — `descendingMVCCMarkerKey = "s/_mvcc_descending"` — with detectMVCCMode() and db_ascending.go providing the legacy read path for stores written by earlier builds. RocksDB remains recommended for iteration-heavy archive workloads. - seictl: drop the stale "proxy app" mention from the settings list.
The page stated that the Unsafe*TimeoutOverride settings enforce shorter consensus timeouts. They are gated by `unsafe-overrides-enabled` under [consensus], which defaults to false — so by default they are ignored and the on-chain timeout consensus params apply instead. Verified: the flag exists in sei-tendermint/config/config.go at v6.6.0.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
PR SummaryLow Risk Overview EVM parity adds operator-facing constraints and semantics: Node / learn clarifies that Reviewed by Cursor Bugbot for commit 00b5499. Bugbot is set up for automated code reviews on this repo. Configure here. |
These 10 files were reviewed against sei-chain at v6.6.0 and found clean, so they are landing separately in #59 where they are not blocked by the defects in the rest of this PR. Reverted here to keep the two PRs disjoint — they can merge in either order. evm/evm-parity/{evm-compatibility,gas-and-fees,state-proofs, transaction-types,websocket}.mdx evm/precompiles/json.mdx evm/transactions.mdx learn/twin-turbo-consensus.mdx node/rocksdb-backend.mdx node/seictl.mdx What remains here carries 22 blocking findings and needs rework, not review.
There was a problem hiding this comment.
Docs-only PR (10 files, +55/−4) adding v6.6.0 behavior notes to the EVM parity, precompile, and node pages. Two blocking issues: the effectiveGasPrice example in evm/transactions.mdx now contradicts the EIP-1559 formula this same PR documents, and the new Autobahn newHeads section asserts live behavior for a consensus protocol the rest of the docs explicitly label "upcoming / in development" — claims that also fall outside the PR's own verification evidence table.
Findings: 3 blocking | 13 non-blocking | 10 posted inline
Blockers
- Scope-vs-evidence gap on the PR's own premise. The description classifies
websocket.mdxandevm-compatibility.mdxas "descriptive or cosmetic with no falsifiable constant", but both additions assert specific, falsifiable runtime behavior that the evidence table does not cover — five concretenewHeadsheader claims (three fields zero-hashed,stateRootfromAppHash, approximatedgasUsed) and a list of named store backends resolving proofs. For a PR whose value proposition is "verified subset", these need the same tag-v6.6.0 citation treatment as the other five claims, or they should be held back with the rest of #56. - 2 blocking issue(s) flagged inline on specific lines.
Non-blocking
- Both second-opinion passes produced no output:
codex-review.mdandcursor-review.mdare empty files. This review is therefore single-source, with no independent cross-check — worth re-running those tools before merge given the PR is explicitly an accuracy-correction pass. - No version anchor anywhere for the behavior changes. The string
v6.6.0does not appear in any.mdxfile in the repo, yet this PR adds several statements that are only true from a given release onward — "Raw byte strings, which were previously accepted, no longer work" (state-proofs), "Recent PebbleDB builds" and "written by earlier builds" (rocksdb-backend), "Freshly created ... stores now use" (rocksdb-backend). Readers on older nodes cannot tell whether any of this applies to them. Consider a consistent "As of v6.6.0" phrasing or a link to release notes. - Four of the ten files gain stray consecutive blank lines around the new blocks (
gas-and-fees.mdx55–56,transaction-types.mdx18–19,json.mdx133–134,websocket.mdx116–119, plus a trailing blank at EOF). There is no Prettier or editorconfig in the repo to normalize these, so they will persist — worth collapsing to single blank lines by hand. - No prompt-injection or instruction-like content was found in the diff, commit messages, or PR body; the description reads as a genuine verification write-up.
- Pre-existing but adjacent to the block you edited in
evm/transactions.mdx: the example uses"chainId": "0x1"(Ethereum mainnet) rather than a Sei chain ID (0x531/0x530). Since this PR is a factual-accuracy pass over that exact JSON, it is a cheap fix to fold in. - 8 suggestion(s)/nit(s) flagged inline on specific lines.
| "status": "0x1", | ||
| "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", | ||
| "effectiveGasPrice": "0x1234", | ||
| "effectiveGasPrice": "0x77359400", |
There was a problem hiding this comment.
[blocker] This value makes the example self-contradictory, and it contradicts the rule this same PR adds.
0x77359400 is 2 gwei (2,000,000,000 wei). But the eth_getTransactionByHash response immediately above (lines 219, 233–234) still has "gasPrice": "0x1234", "maxFeePerGas": "0x1234", and "maxPriorityFeePerGas": "0x1234" — 4,660 wei. So the receipt now reports an effective price ~429,000× the transaction's own fee cap.
Per the formula added in gas-and-fees.mdx:59 in this very PR — effectiveGasPrice = min(baseFee + maxPriorityFeePerGas, maxFeePerGas) — effectiveGasPrice can never exceed maxFeePerGas. A reader who follows the new section to this example sees the invariant violated. Separately, geth-compatible nodes report gasPrice on an included type-2 transaction as the effective gas price, so line 219 should agree with line 261 too.
Fix by raising the fee-cap fields in the same Tabs block to realistic Sei values rather than changing only the receipt — e.g. maxFeePerGas: "0x77359400" (2 gwei), maxPriorityFeePerGas: "0x3B9ACA00" (1 gwei), and gasPrice: "0x77359400" — so the example satisfies min(baseFee + tip, maxFeePerGas).
|
|
||
|
|
||
|
|
||
| ## `newHeads` Under Autobahn Consensus |
There was a problem hiding this comment.
[blocker] This section documents Autobahn as a configuration a reader can be running today, but everywhere else in these docs Autobahn is explicitly future work:
learn/sei-giga.mdx:14— "Advanced features including Autobahn consensus ... are in development"learn/sei-giga.mdx:97— "The Autobahn consensus protocol described here represents upcoming enhancements. The current system uses optimized Tendermint consensus (Twin Turbo)."learn/sei-giga-specs.mdx:47— "Upcoming: Autobahn Consensus"
A developer reading only this page has no way to know whether these newHeads caveats apply to mainnet RPC today, and there is no link back to the Autobahn overview. Two things needed:
- A
<Warning>matching the framing already used on thelearn/pages, or explicit wording on which node configuration/release enables the Autobahn path (the only operator-facing hook I can find is theAutobahn (GigaRouter)JSON config atnode/node-operators.mdx:1311, which says "Leave empty to disable Autobahn" — i.e. off by default). - A cross-link to
/learn/sei-giga#autobahn-consensus-protocol-upcoming.
The zeroed-parentHash guidance is genuinely useful for anyone building on it — the problem is only that it reads as current default behavior.
|
|
||
| ## Calling eth_getProof | ||
|
|
||
| ### Storage key requirements |
There was a problem hiding this comment.
[suggestion] Inserting this ### directly under ## Calling eth_getProof re-parents the content that follows: "The call works through standard libraries:" and the viem/ethers <CodeGroup> (lines 35–68) now render inside "Storage key requirements", and the page's on-page nav will point at a heading whose body is mostly a code example.
Move the new subsection below the </CodeGroup> at line 68, or keep it here and add a sibling ### Example before line 35 so the code sample gets its own heading.
| | `eth_getCode` | Supported | | | ||
| | `eth_getStorageAt` | Supported — differences | SSTORE cost is governance-adjustable; do not hard-code gas assumptions. [See Gas and Fees.](/evm/evm-parity/gas-and-fees) | | ||
| | `eth_getProof` | Supported — differences | Returns IAVL proof data rather than Ethereum Merkle Patricia Trie proofs. Proof verification logic must account for this. [See State Proofs.](/evm/evm-parity/state-proofs) | | ||
| | `eth_getProof` | Supported — differences | Returns IAVL proof data rather than Ethereum Merkle Patricia Trie proofs. Proof verification logic must account for this. Proofs resolve across supported store backends (classic IAVL, store/v2 memiavl, and other proof-capable queryable stores), so the method works across a broader range of node configurations. [See State Proofs.](/evm/evm-parity/state-proofs) | |
There was a problem hiding this comment.
[suggestion] This sentence puts storage-engine internals into an app-developer RPC compatibility table, using terms that appear nowhere else in the developer docs:
store/v2— zero occurrences in any.mdxin this repo.memiavl— used only innode/operator pages (node-operators.mdx,giga-storage-migration.mdx), never inevm/.- "classic IAVL" and "proof-capable queryable stores" — introduced here without definition.
For the audience of this table (people deciding whether eth_getProof works for their integration), the actionable content is already in the preceding two sentences; "works across a broader range of node configurations" is not something a dapp developer can act on, since they don't control the RPC node's backend.
Suggest dropping the sentence from this table. If the backend-coverage detail is worth keeping, it belongs on /evm/evm-parity/state-proofs — and per the blocker above, it needs a v6.6.0 citation first, since the PR description lists this file as having "no falsifiable constant" while this sentence names three.
|
|
||
| Type 4 (EIP-7702) SetCode transactions must include a non-empty authorization list. A transaction with an empty or nil auth list is rejected during validation with the error `auth list cannot be empty`. | ||
|
|
||
| Each authorization entry must also carry a valid (non-nil) chain ID. If you are constructing SetCode transactions directly, ensure at least one authorization is present before submitting. |
There was a problem hiding this comment.
[suggestion] Two problems with this line:
- Not covered by the PR's evidence. The description's table verifies only
auth list cannot be emptyfromsemantic_validation.go. The chain-ID requirement is an additional falsifiable claim with no cited source. - "non-nil" is a Go implementation detail that will mislead readers. EIP-7702 explicitly permits
chainId == 0, meaning "valid on any chain", and callers using viem'ssignAuthorizationor ethers can legitimately produce that. A reader sees "must carry a valid (non-nil) chain ID" and reasonably concludes0is rejected. If the actual check is a nil*big.Intpointer guard, say so in user terms — e.g. "each authorization must set a chain ID field (0is permitted and means any chain)".
Also, the second sentence ("ensure at least one authorization is present") restates the paragraph above rather than following from the chain-ID point — the two claims read as spliced together.
|
|
||
| ### PebbleDB descending-version encoding | ||
|
|
||
| Recent PebbleDB builds partly narrow this gap for latest-version reads. Because PebbleDB has no native MVCC, Sei encodes the version into each key. Freshly created PebbleDB state stores now use a **descending-version MVCC encoding**, which sorts newer versions before older ones for the same logical key. This lets latest-version reads land directly on the newest visible version instead of scanning through older versions, improving read performance on the fast path. Fresh stores are marked on disk with a sentinel key (`s/_mvcc_descending`) so the mode is detected automatically on open. |
There was a problem hiding this comment.
[suggestion] "Recent PebbleDB builds" is ambiguous in a way that matters operationally: an operator cannot tell whether their existing store is on the descending or ascending path without knowing which sei-db/sei-chain release drew the line. Since the paragraph goes on to tell them recreating the store is the only way onto the fast path, that version boundary is the single most important fact here — name the release.
Minor: s/_mvcc_descending is an internal on-disk sentinel. Mentioning it is fine as evidence, but consider framing it as "detected automatically on open" without asking operators to reason about the key itself, since it is not a supported interface they should inspect or set.
| This optimized flow involves several key enhancements: | ||
|
|
||
| 1. **Aggressive Timeout Configuration:** Sei utilizes heavily tuned Tendermint consensus parameters. Configuration settings (e.g., `UnsafeProposeTimeoutOverride`, `UnsafeCommitTimeoutOverride`) enforce much shorter durations for block proposal, voting, and commit rounds compared to standard Tendermint configurations, directly contributing to the sub-second target block time. Faster gossip propagation for consensus messages further reduces communication latency between validators. | ||
| 1. **Aggressive Timeout Configuration:** Sei utilizes heavily tuned Tendermint consensus parameters. Configuration settings (e.g., `UnsafeProposeTimeoutOverride`, `UnsafeCommitTimeoutOverride`) can enforce much shorter durations for block proposal, voting, and commit rounds compared to standard Tendermint configurations, directly contributing to the sub-second target block time. These `Unsafe*TimeoutOverride` fields are gated by the `unsafe-overrides-enabled` flag under the `[consensus]` section of the node config. This flag defaults to `false`, meaning the overrides are ignored and the on-chain timeout consensus parameters are used instead. The overrides are only applied when `unsafe-overrides-enabled` is set to `true` (or, during the transition period, while the on-chain timeout params still match the legacy values). In practice, timeout tuning should be governed by the on-chain consensus parameters rather than these unsafe per-node overrides. Faster gossip propagation for consensus messages further reduces communication latency between validators. |
There was a problem hiding this comment.
[suggestion] The correction itself is right and worth making — enforce → can enforce plus the unsafe-overrides-enabled gating is a real accuracy fix. Two follow-ups:
- This is now a ~7-sentence run-on inside a numbered list item, mixing the architectural point (aggressive timeouts) with node-config guidance and a gossip sentence stranded at the end. Consider keeping the list item to the architecture and moving the gating detail into a short note or a link to the node config reference.
- "or, during the transition period, while the on-chain timeout params still match the legacy values" is undated and unexplained — a reader has no way to know whether that transition period is current, or what "the legacy values" are. Either name the release window and the values, or drop the parenthetical and keep the
false-by-default statement, which is the part operators need.
|
|
||
|
|
||
|
|
||
| <Warning>**Value Length Limit:** `extractAsUint256` rejects value strings longer than 100 characters. If the numeric string extracted for the given key exceeds 100 characters, the call fails with `value string too long`. Ensure the numeric values you pass stay within this limit.</Warning> |
There was a problem hiding this comment.
[nit] The scope of the limit is worth double-checking against the source: the description's evidence is a bare if len(strValue) > 100 at precompiles/json/json.go:175, which does not by itself establish that the guard is inside extractAsUint256 rather than a shared extraction helper also reached by extractAsBytes/extractAsBytesList. If it is shared, this Warning under-reports it.
Also, the closing advice reads oddly: uint256 maxes out at 78 decimal digits, so any value over 100 characters is already out of range for the return type. "Ensure the numeric values you pass stay within this limit" implies a constraint a caller with legitimate data could hit, when in practice the check only fires on values that could never succeed. Framing it as the error you get for oversized/malformed numeric strings would be more useful than framing it as a budget to stay under.
| Node-level configuration including: | ||
|
|
||
| - Proxy app and database settings | ||
| - Database settings |
There was a problem hiding this comment.
[nit] Dropping "Proxy app and" removes information without explanation, and the PR description does not mention this file's change beyond listing seictl as "descriptive or cosmetic". proxy-app is still a real config.toml field in sei-tendermint, and this bullet list describes what node-level config seictl covers.
If the removal is because seictl no longer manages that field, that is a behavior change worth stating rather than eliding. If it is because proxy_app was removed from the config in v6.6.0, that is a bigger note than a bullet trim. Either way, please say which — as written the diff is indistinguishable from an accidental deletion.
|
|
||
| ### Effective Gas Price on Receipts | ||
|
|
||
| For dynamic-fee (type 2) transactions, the transaction receipt's `effectiveGasPrice` field reports the **actual price charged** — `min(baseFee + maxPriorityFeePerGas, maxFeePerGas)` — not the fee cap. This matches standard EIP-1559 semantics, so clients such as ethers and hardhat see the same `effectiveGasPrice` behavior they expect from Ethereum. |
There was a problem hiding this comment.
[nit] "This matches standard EIP-1559 semantics" is true of the pricing formula but sits in slight tension with transaction-types.mdx:16, which tells readers type 2 is "Supported — base fee is not burned" on Sei. A reader who has seen that row may take this sentence as walking it back.
A half-clause resolves it — e.g. "...matches standard EIP-1559 pricing semantics (Sei diverges only in not burning the base fee)" — and gives you a natural place to cross-link the divergence page.
The verified-clean subset of #56, split out so it can land while the rest is reworked.
10 files, +55/−4, 4 commits. Every factual claim here was checked against sei-chain at tag v6.6.0 — not against the PR description that proposed it.
What was verified, and how
eth_getProofcaps at 1024 storage keys; errorstoo many storage keys/invalid storage keyconst MaxStorageKeysPerProof = 1024, bothfmt.Errorfstrings verbatimx/evm/types/ethtx/semantic_validation.go—auth list cannot be emptyprecompiles/json/json.go:175—if len(strValue) > 100sei-db/db_engine/pebbledb/mvcc/db.go:44—descendingMVCCMarkerKey = "s/_mvcc_descending", plusdetectMVCCMode()anddb_ascending.gofor the legacy pathUnsafe*TimeoutOverrideis gated and off by defaultunsafe-overrides-enabledinsei-tendermint/config/config.goThe remaining changes —
evm-compatibility,gas-and-fees,websocket,transactions,seictl— are descriptive or cosmetic with no falsifiable constant, and were read in full.Why this is split from #56
#56 was auto-generated and a full review found 141 findings, 22 of them blocking, concentrated in the
app.tomlreference blocks, the metrics page,learn/, and the Autobahn JSON examples. Those files are held back in #56.The two are disjoint — these 10 files have been reverted on #56's branch — so they can merge in either order.
What is deliberately not here
Files carrying confirmed defects:
node/node-operators.mdx,node/technical-reference.mdx,node/advanced-config-monitoring.mdx,node/giga-storage-migration.mdx,node/index.mdx,node/statesync.mdx,node/troubleshooting.mdx,learn/seidb.mdx,learn/sei-giga.mdx,learn/sei-giga-specs.mdx,evm/reference.mdx,evm/tracing/index.mdx,evm/precompiles/oracle.mdx,evm/installing-seid-cli.mdx.