Epochnotes

A field journal for network changes

Read the layers
of change.

A signed registry of network changes on Solana, Ethereum and Base. Understand what breaks in your code, where, and how to fix it.

Epochnotes / A record of changing epochs
5 entries · Solana / Ethereum / Base · Signed log v1Pre-release · Not audited · Read the signed manifest ↗

How it works

Signed entries

Each change is an entry built from primary sources — a SIMD or an EIP, the client's code, the chain itself. A version of the registry is a Merkle root, signed by its publisher and anchored by a program on Solana devnet.

Checks

check repo finds the code a change breaks; check rpc asks a provider, read-only, whether it behaves as the entry says; registry read --status reads the activation from each chain at the moment you ask.

Watcher

Every day it reads the feature gates agave declares and their state on three Solana clusters, and drafts an entry for what moved outside the registry. A person finishes the draft from the SIMD and signs it.

Ask your AI agent

The Epochnotes skill lets you ask in plain words: is this upgrade live, and does my project break? Your agent answers from the signed registry, not from what it remembers, and checks your code with the same tool. It works with any agent that can run a command in your terminal: Claude Code, Codex and others.

Answers from the registry

Every answer names the registry version, its signature and the slot or block it read the status from. If the registry cannot be verified, it does not answer at all.

Checks your whole project

Code, contracts, lockfiles and your RPC provider: it runs the same checks as the command line and points at the line to fix.

Says what it did not check

A rule that could not run, a file it did not read, a guess of its own: each is named, so silence never reads as a clean result.

Install in a minute

Get the skill once:

git clone --depth 1 https://github.com/mskrad/Epochnotes ~/epochnotes-src

Then add it to your agent. The skill is one folder with a SKILL.md file, the common format for agent skills.

# Claude Code (terminal, or the Code tab of Claude Desktop)
mkdir -p ~/.claude/skills && cp -R ~/epochnotes-src/skills/epochnotes ~/.claude/skills/

# Codex
mkdir -p ~/.codex/skills && cp -R ~/epochnotes-src/skills/epochnotes ~/.codex/skills/

# Any other agent: add this line to the project's AGENTS.md (or its own instructions file)
For questions about network upgrades on Solana, Ethereum or Base, follow ~/epochnotes-src/skills/epochnotes/SKILL.md.

Optional: point it at the published log before you start the agent. Without this the skill names the published log and asks you first, because the publishers file is your trust anchor.

export EPOCHNOTES_VERSIONS=https://mskrad.github.io/Epochnotes/registry/versions
curl -fsS https://mskrad.github.io/Epochnotes/registry/publishers.json -o ~/.epochnotes-publishers.json
export EPOCHNOTES_PUBLISHERS=~/.epochnotes-publishers.json

Then ask

Is transaction v1 live on Solana mainnet, and does this project break?
What does EIP-7702 change for my contracts on Base?
Check this project against recent network changes.

In Claude Code you can also call the skill by name: /epochnotes followed by the question.

A real answer

From a run on Oct 12, 2026, in Claude Code on a small demo app; shortened.

Yes, transaction v1 is live on Solana mainnet, and my-app breaks.
my-app/src/history.ts:6 calls getTransaction with maxSupportedTransactionVersion: 0.
Fix (from the entry): pass 1; with @solana/web3.js use 1.99.0 or later.

Status:   active on mainnet-beta since slot 447120000, read at slot 456149984 (feature account)
Registry: version 1, root 77216c0c..., signature verified, on-chain anchor matches
Not checked: the web3.js version (my-app has no lockfile); the runtime probe

What it found in public code

We ran epochnotes check repo over 59 public Solana repositories, checked every hit by hand and reproduced each breakage on mainnet before reporting it. 9 reported, 2 already fixed and merged.

ProjectWhat brokeReportOutcome
solana-com
solana-foundation
The slot-time page read blocks with maxSupportedTransactionVersion 0, which the RPC refuses once a block holds a v1 transaction. issue #2141 merged fix #2150
2026-10-07
core-ai
helius-labs
The MCP server and CLI asked for full blocks with maxSupportedTransactionVersion 0, so getBlock failed on v1 blocks. issue #183 merged fix #190
2026-10-14
openzeppelin-monitor
OpenZeppelin
The Solana monitor could not load nearly any recent mainnet block: every getBlock failed with -32015. PR #515 open PR #515
2026-10-23
wormhole-sdk-ts
wormhole-foundation
parseTransaction and parseMessages threw on v1 transactions, and Wormhole messages are already posted in them. PR #1049 open PR #1049
2026-10-23
ZenPlugins
zenmoney
Solana wallet sync stopped with a TypeError when the sync window held one v1 transaction, even an incoming one. PR #1213 open PR #1213
2026-10-23
photon-explorer
helius-labs
The explorer showed "Failed to load transaction" for any v1 transaction. PR #5 open PR #5
2026-10-23
public-v4-client
Squads-Protocol
The multisig lookup stopped at the first v1 transaction in a vault's history. issue #48, PR #49 open PR #49
2026-10-22
solana-new
sendaifun
The debug-program agent skill taught a getTransaction call that fails on v1 transactions. PR #62 open PR #62
2026-10-23
solana-mpp
sendaifun
Payment verification threw for a payment sent as a v1 transaction. PR #7 open PR #7
2026-10-23

Outcomes as of 2026-10-23. An open pull request is a proposal, not an acceptance.

What the Solana watcher sees

Read on 2026-10-15 from agave 929e9fb658d8 and three clusters (mainnet-beta slot 457175726, testnet slot 452698980, devnet slot 511293223): 290 feature gates declared, 11 of them covered by the registry.

On the way to mainnet-beta (5)

Not active on mainnet-beta yet, but active on testnet or devnet, or already scheduled on mainnet-beta.

GateSIMDmainnet-betatestnetdevnetEntry
virtual_address_space_adjustments
SIMD-0460: Virtual Address Space Adjustments
SIMD-0460 unknownactiveactive not yet
account_data_direct_mapping
enable account data direct mapping
— absentactiveactive not yet
alpenglow
SIMD-0326: Alpenglow: new consensus algorithm
SIMD-0326 absentactiveactive alpenglow
direct_account_pointers_in_program_input
SIMD-0449: Direct Account Pointers in Program Input
SIMD-0449 absentactiveactive not yet
enable_sha512_syscall
SIMD-0512: SHA512 Syscall
SIMD-0512 absentactiveactive not yet

The registry (5 entries)

Alpenglow

SIMD-0326Solanaentry alpenglow · rev 1

What breaks

  • stream Votes stop being transactions on chain: validators send them directly to each other. Initially a validator admission ticket reproduces the incentives of today's vote transaction fees. Voting is not invisible afterwards: for rewards, the leader of slot s+8 can post up to two vote aggregates for slot s. Code that reads individual vote transactions out of blocks no longer finds them.
  • rpc Optimistic confirmation is superseded by faster actual finality; the SIMD names this as the most visible change.

How to fix it

  • Do not derive validator activity from vote transactions in blocks, and review code that relies on optimistic confirmation as something distinct from finality.
Activations (1) and sources (2)
  • alpenglow — feature gate A1pengvuM6JEcyNuTnMqepBKhwHE3N6PmUrdATGawhJS
    Feature gate registered in agave as "SIMD-0326: Alpenglow: new consensus algorithm".

Set Code for EOAs

EIP-7702EthereumBaseentry eip-7702 · rev 1 · 3 static rule(s), 2 RPC probe(s)

What breaks

  • program EIP-7702 breaks the invariant that msg.sender == tx.origin holds only in the top frame of a transaction: a delegated account can make several calls in one transaction. Reentrancy guards of the style require(tx.origin == msg.sender) and atomic sandwich protections that rely on tx.origin no longer hold. Using that check only to know that the sender is an EOA is, by the EIP's own reading, not affected.
  • program An account delegated under EIP-7702 has code: the delegation indicator, 0xef0100 followed by the address of the delegate, and EXTCODESIZE returns 23 for it. A check that reads code as "a contract" or no code as "an EOA" — an isContract call, a test of code.length or extcodesize — now takes a delegated EOA for a contract.
  • program OpenZeppelin's SignatureChecker.isValidSignatureNow checks an ECDSA signature only when the signer has no code, and otherwise asks the signer for ERC-1271. A delegated EOA has code, so its ECDSA signature is not checked as such: it is valid only if the delegate implements ERC-1271 and accepts it.
  • wallet OpenZeppelin's ERC721Utils.checkOnERC721Received calls onERC721Received on any recipient with code, and reverts when the call fails or returns another value. A safe transfer or a safe mint to a delegated EOA reverts unless its delegate implements the receiver.
  • sdk eth_getCode of a delegated EOA returns the 23-byte delegation indicator, and the account still sends transactions. Telling contracts from EOAs by whether eth_getCode is empty no longer works.

How to fix it

  • Replace a reentrancy guard built on tx.origin == msg.sender with one held in a transient storage variable, as the EIP suggests. A sandwich protection built on tx.origin has no replacement in the EIP, which calls relying on it a bad practice: it has to be designed again, not patched.
  • Where it matters whether an account is a delegated EOA, read the first three bytes of its code: 0xef0100 marks the delegation indicator (OpenZeppelin's EIP7702Utils.fetchDelegate returns the delegate), and decide explicitly how such an account is treated — for a signature, ECDSA, ERC-1271, or both.
  • A contract meant to be a delegate implements the receiver hooks the accounts that delegate to it rely on, such as onERC721Received: OpenZeppelin's ERC721Utils calls it on every recipient with code.
Activations (2) and sources (16)
  • prague — ethereum at 2025-05-07T10:05:11Z
    Prague, the execution layer of the Pectra upgrade: go-ethereum sets PragueTime to this time for mainnet, and EIP-7600 lists EIP-7702 and EIP-7685 among its EIPs.
  • isthmus — base at 2025-05-09T16:00:01Z
    Isthmus, the OP Stack upgrade that brings the Pectra execution layer, EIP-7702 included, to the chain; on OP Stack chains requestsHash is always sha256(''), the hash of no requests, so its presence marks the fork.

Incrementally Reduce lamports_per_byte to 696

SIMD-0437Solanaentry rent-simd-0437 · rev 1

What breaks

  • economics The minimum balance of an account is (128 + data size in bytes) * lamports_per_byte, and every step lowers lamports_per_byte. An account funded at an earlier, higher minimum keeps the difference: the SIMD allows reducing the balance to the new minimum, it does not return anything by itself.
  • sdk A lamports_per_byte value compiled into a client or program goes stale at every step: on activation the value changes in the bank and in the Rent sysvar, while DEFAULT_LAMPORTS_PER_BYTE in the SDKs is updated only after activation. The SIMD calls the reduction strictly a relaxation: existing program logic keeps working, so a stale higher constant overpays rather than fails.
  • program The reduction is not guaranteed to be one-way. The SIMD states that rent can be increased back to the legacy value in case of excessive state growth, noting that SIMD-0392 allows this without significant disruption to existing accounts; agave already declares a gate set_lamports_per_byte_to_6960, registered as "SIMD-0438: Reset lamports per byte to legacy value of 6960". A minimum balance fixed at a reduced value can therefore become too low for newly created accounts.

How to fix it

  • Take the minimum balance from the cluster at run time (the Rent sysvar in programs, getMinimumBalanceForRentExemption in clients) instead of a compiled-in lamports_per_byte.
Activations (5) and sources (3)
  • set_lamports_per_byte_to_6333 — feature gate 4a6f7o7iTcA8hRDCrPLkSatnt5Ykxiu36wo5p1Tt12wC
    Step 1 of 5. Sets lamports_per_byte to 6333 (from 6960).
  • set_lamports_per_byte_to_5080 — feature gate 61BtM7BkDEE8Yq5fskEVAQT9mYA8qCejJWoLe5apqg81
    Step 2 of 5. Sets lamports_per_byte to 5080.
  • set_lamports_per_byte_to_2575 — feature gate rntCigrTppP5JdZz7K8TyN9sMzLdAcXp8SejYpVpX6D
    Step 3 of 5. Sets lamports_per_byte to 2575.
  • set_lamports_per_byte_to_1322 — feature gate rntD7invRBswCAdKtRsh1G4psKjrPdS3BKqtnA78C7N
    Step 4 of 5. Sets lamports_per_byte to 1322.
  • set_lamports_per_byte_to_696 — feature gate rntTjNZ9boq8owDxjGVFHPfWNQPDaKiM5JcjxmDGg47
    Step 5 of 5. Sets lamports_per_byte to 696.

Reduce Slot Times

SIMD-0525Solanaentry slot-duration · rev 1

What breaks

  • program A duration expressed in slots shrinks in wall-clock time with every stage: the SIMD tells dapp developers and off-chain services to stop assuming that 400 ms * slots is a stable conversion. A timer of N slots lasts half as long at 200 ms as it did at 400 ms.
  • sdk SDK constants stay at their 400 ms values at first: static constants for default slot duration, ticks per second, slots per year and slot-to-time conversion may keep describing 400 ms while the cluster runs 350, 300, 250 or 200 ms banks.
  • economics Per-slot limits scale down in proportion to the slot time so that the wall-clock rate stays approximately the same. From the 400 ms baseline of 60,000,000 the maximum block compute units become 37,500,000 at 250 ms and 30,000,000 at 200 ms; the maximum writable-account compute units go from 24,000,000 to 15,000,000 and 12,000,000. The ratios apply to whatever baseline is active: with the 100,000,000 block limit of SIMD-0286 the values are 62,500,000 and 50,000,000. Code tuned to the per-block or per-account limit of a 400 ms slot exceeds the scaled one.
  • program An epoch stays 432,000 slots, so it lasts half as long at 200 ms as at 400 ms. Anything scheduled per epoch happens twice as often in wall-clock time.

How to fix it

  • Keep slot counts only where slot semantics are intended; where a duration means wall-clock time, do not derive it from a fixed milliseconds-per-slot constant. The SIMD advises preferring cluster-provided timing parameters once they are available.
Activations (4) and sources (2)
  • reduce_slot_time_to_350ms — feature gate iBRL5RuWhw4yqaAZu96RUULHckHTZAoe2b77qaV38JZ
    Target slot time 350 ms. Takes effect one epoch after the gate is activated.
  • reduce_slot_time_to_300ms — feature gate iBRLL3k18HST852F1Mf3Lv83waTNQmmqvKDxvYGwQFL
    Target slot time 300 ms. Takes effect one epoch after the gate is activated.
  • reduce_slot_time_to_250ms — feature gate iBRLMc81UjRa8fn8A6eE8bJTnRbgQoPTynM51akENCV
    Target slot time 250 ms. Takes effect one epoch after the gate is activated.
  • reduce_slot_time_to_200ms — feature gate iBRLjhJnkmDZgNoZRDMW11d8ZV7HvsL3vAyRjZB5npW
    Target slot time 200 ms. Takes effect one epoch after the gate is activated.

Transaction V1 Format

SIMD-0385Solanaentry tx-v1 · rev 1 · 9 static rule(s), 1 RPC probe(s)

What breaks

  • rpc getTransaction, and getBlock with transactionDetails full or accounts, fail with JSON-RPC error -32015 "Transaction version (1) is not supported by the requesting client" when the request omits maxSupportedTransactionVersion or sets it to 0 and the result contains a version 1 transaction. getBlock with transactionDetails signatures or none is not affected.
  • decoder A serialized version 1 transaction starts with the version byte 129 (0x81). A decoder written for the legacy and version 0 layouts has no branch for that value.
  • sdk @solana/kit 8.0.0 removed three compute-unit-limit helpers from @solana/kit in favour of resource-limit helpers that also set the loaded accounts data size limit for version 1 transactions, and removed the fixed transaction size constants from @solana/transactions because version 1 transactions have a larger size limit.
  • decoder The maximum size of a version 1 transaction is 4096 bytes; the prior maximum was 1232. A buffer or a size check fixed at 1232 bytes is too small for a valid version 1 transaction.
  • sdk @solana/web3.js 1.98.4 cannot read a version 1 transaction even when maxSupportedTransactionVersion is 1: getTransaction and getParsedTransaction throw a StructError on the version field, and getBlock fails the same way. 1.99.0 reads all three and returns a MessageV1.

How to fix it

  • Pass maxSupportedTransactionVersion 1 in getTransaction and getBlock; with @solana/web3.js this also needs version 1.99.0 or later.
  • When moving to @solana/kit 8.0.0 or later, replace the compute-unit-limit helpers with the resource-limit helpers named in the release notes.
  • Set the maximum supported transaction version to 1 in the configuration of the RPC client (Some(1) in Rust, rpc.MaxSupportedTransactionVersion1 or uint64(1) in Go, max_supported_transaction_version=1 in Python), and check that the client library in use can decode a version 1 transaction.
Activations (1) and sources (7)
  • enable_tx_v1 — feature gate txv1aq4pp281K9um3tnPgkfX8UqtFT6wcVW3hNezGLL
    Feature gate registered in agave as "SIMD-0385: Transaction V1".

Check it yourself

Install the CLI with npm install -g @epochnotes/cli, then read from the published signed log:

export EPOCHNOTES_VERSIONS=https://mskrad.github.io/Epochnotes/registry/versions
curl -fsS https://mskrad.github.io/Epochnotes/registry/publishers.json -o epochnotes-publishers.json
export EPOCHNOTES_PUBLISHERS=./epochnotes-publishers.json
epochnotes registry read --json --status mainnet-beta   # verified entries and the state of each activation
epochnotes registry read --status base                  # the same entries, read on Base
epochnotes check repo <path>                            # where a change breaks this code
epochnotes check rpc --rpc-url <endpoint>               # does this provider behave as the entry says

When it cannot verify what it reads, it refuses to answer rather than guess.

Roadmap

Now

  • Entries for Solana, Ethereum and Base in one signed log, anchored and verified against the program on Solana devnet
  • Free and open source (MIT): a public good for developers
  • Static rules for TypeScript, JavaScript, Rust, Go, Python, Solidity and lockfiles; read-only RPC probes
  • Rent held above the minimum: scan, close plan, withdraw template
  • An agent skill for Claude Code, Codex and others that answers from the verified registry only
  • The Solana watcher, run daily by a scheduled Claude task that drafts from the SIMD

Next

  • status and watch read the signed log, not only a clone of the repository
  • Watchers for Ethereum and Base: fork times in the client configurations
  • A notification when something outside the registry moves
  • This page updated from each watcher report
  • Entries for the gates already on the way to mainnet-beta