> ## Documentation Index
> Fetch the complete documentation index at: https://docs.optimism.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrade 19 - Karst Hard Fork

> Learn how to prepare for the Karst hard fork.

The Karst hard fork is a proposed network upgrade for OP Stack chains. It **introduces the L2 Contract Manager (L2CM)** enabling governance-approved L2 smart contract upgrades through a new consensus layer mechanism, **promotes `CANNON_KONA` to the respected game type** making the Rust-based `kona-client` the primary fault proof program used for withdrawals, **activates Osaka EIPs on L2** including the EIP-7825 transaction gas limit, and **adds a preemptive Glamsterdam Defense change** reducing the BN256 pairing precompile maximum input size from 427 to 300 pairs.

<Warning>
  **`op-geth` and `op-program` will not support Upgrade 19 / Karst.** Migrate to **`op-reth`** (and **`kona-client`** for fault proofs) before activation. End of Support is **May 31, 2026** — see [End of Support for op-geth and op-program](/notices/op-geth-deprecation).
</Warning>

<Info>
  The Karst hard fork on Optimism Governed **Sepolia OP Stack chains** will be activated on **Wed, Jun 17, 2026 at 16:00:01 UTC** (`1781712001`). On **Mainnet OP Stack chains** the upgrade is planned to optimistically activate on **Wed, Jul 8, 2026 at 16:00:01 UTC** (`1783526401`), depending on Optimism Governance's approval. The smart contract upgrades are expected to happen the week prior to activation.
  The upgrade will be executed on the following chains: `OP`, `Soneium`, `Ink`, `Unichain`, `Mode`, `Metal`, and `Zora`.
</Info>

## What's included in Upgrade 19

Upgrade 19 introduces the following changes:

* **L2 Contract Manager (L2CM):** introduces a mechanism for upgrading L2 smart contracts (predeploys) as part of a network upgrade. L2CM enables L2 predeploy upgrades in a structured, auditable way — reducing the need for multisig signing for each individual contract change. This is a prerequisite for interop and future protocol upgrades that need to modify L2 contracts. See the [L2CM feature page](/op-stack/features/l2-contract-manager), [design doc](https://github.com/ethereum-optimism/design-docs/blob/main/protocol/l2-contract-upgrades.md), and [FMA](https://github.com/ethereum-optimism/design-docs/blob/main/security/fma-l2cm.md) for details.

* **Osaka on L2:** activates a subset of Osaka EIPs on L2, including [EIP-7825](https://eips.ethereum.org/EIPS/eip-7825) (per-transaction gas limit of 2^24 = 16,777,216 gas), [EIP-7883](https://eips.ethereum.org/EIPS/eip-7883) (MODEXP gas floor increase), [EIP-7823](https://eips.ethereum.org/EIPS/eip-7823) (MODEXP input size cap), [EIP-7951](https://eips.ethereum.org/EIPS/eip-7951) (P256VERIFY gas cost increase), and [EIP-7939](https://eips.ethereum.org/EIPS/eip-7939) (CLZ opcode). See the [Osaka on L2 feature page](/op-stack/features/osaka-on-l2), [design doc](https://github.com/ethereum-optimism/design-docs/blob/main/protocol/osaka-on-l2.md), and [Karst spec](https://specs.optimism.io/protocol/karst/overview.html) for details.

* **Glamsterdam Defense (BN256 pairing input size reduction):** as a preemptive measure ahead of the Glamsterdam L1 hard fork, Upgrade 19 includes a hard fork change that reduces the maximum input size for BN256 pairing precompile calls from 427 pairs (81,984 bytes) to 300 pairs (57,600 bytes). This provides additional headroom against potential last-minute gas repricing increases in [EIP-7904](https://eips.ethereum.org/EIPS/eip-7904) that could cause fault proof verification to exceed the [EIP-7825](https://eips.ethereum.org/EIPS/eip-7825) L1 transaction gas limit. See [analysis](https://github.com/ethereum-optimism/optimism/issues/20195) for details. The full Glamsterdam Defense smart contract changes (absolute pre-state upgrade) are deferred and will be slotted in once final L1 gas pricing values are confirmed.

* **`CANNON_KONA` set to respected game type:** changes the respected game type from `CANNON` (0) to `CANNON_KONA` (8), making the Rust-based `kona-client` the primary fault proof program used for withdrawals. The `CANNON` (0) game type is retained only to resolve in flight games created before the upgrade. `op-program` is no longer supported for new fault proofs. This builds on the `CANNON_KONA` game type support introduced in [Upgrade 18](/notices/archive/upgrade-18).

* **New Kona absolute prestate:** ships an updated `cannon64-kona` absolute prestate hash embedding the latest chain configurations.

* **OPCMv2:** Upgrade 19 is the first upgrade to use OPCMv2, the redesigned OP Contracts Manager. OPCMv2 has a new interface, please take a look at the [source code](https://github.com/ethereum-optimism/optimism/blob/c332a1bafb206f18561173eb13c31f1747f9aea4/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol#L38) for more information.

<Info>
  This upgrade uses OPCMv2, the new default OP Contracts Manager. Chain operators calling `OPCM.upgrade()` directly or using `op-deployer` should ensure they are targeting the correct OPCMv2 contract version.
</Info>

## Breaking Changes

### Osaka on L2: EIP-7825 transaction gas limit, MODEXP and P256VERIFY gas cost changes

After Upgrade 19, L2 transactions are subject to the [EIP-7825](https://eips.ethereum.org/EIPS/eip-7825) per-transaction gas limit of 2^24 = 16,777,216 gas. Transactions requesting gas above this limit will be invalid. Osaka on L2 also increases the gas cost for the `MODEXP` (`0x05`) precompile ([EIP-7883](https://eips.ethereum.org/EIPS/eip-7883)) and introduces a new input size cap ([EIP-7823](https://eips.ethereum.org/EIPS/eip-7823)), increases the gas cost for `P256VERIFY` (`0x100`) from 3,450 to 6,900 gas ([EIP-7951](https://eips.ethereum.org/EIPS/eip-7951)), and adds the CLZ (Count Leading Zeros) opcode ([EIP-7939](https://eips.ethereum.org/EIPS/eip-7939)).

* **Deposit transactions are exempt** from the EIP-7825 limit. Deposits are already capped at 20M gas total per L1 block, and rejecting deposits on L2 that were accepted on L1 could cause permanent ETH loss. Deposits will continue to land on L2 even if their gas limit exceeds the EIP-7825 threshold.
* **For app developers:** if your application submits L2 transactions with very high gas limits (above the EIP-7825 limit), those transactions will no longer be valid after this upgrade. In practice, very few transactions are expected to exceed this limit. If your contracts call `MODEXP` or `P256VERIFY`, verify that your gas estimates account for the updated costs. Contracts relying on hardcoded gas values for these precompiles should be updated. Additionally, `MODEXP` calls with inputs exceeding the new size limit will fail.
* **For node operators:** the `op-reth` execution client must be updated to enforce the new gas limit rules. System deposit transactions (e.g., network upgrade transactions) are also exempt from the limit.

### Glamsterdam Defense: BN256 pairing input size reduction

Upgrade 19 reduces the maximum input size for `ecPairing` (BN256 pairing, `0x08`) precompile calls from 427 pairs (81,984 bytes) to 300 pairs (57,600 bytes). This is a preemptive change to maintain sufficient headroom for L1 fault proof replayability ahead of potential gas repricing in the upcoming [Glamsterdam L1 hard fork (EIP-7904)](https://eips.ethereum.org/EIPS/eip-7904).

* **For app developers:** if your contracts make calls to the BN256 pairing precompile (`0x08`) with more than 300 pairs, those calls will fail after this upgrade. Standard ZK proof verification (e.g., Groth16) uses a small number of pairings and is not affected.

<Info>
  BN256 pairing (`0x08`) is the only variable-input precompile affected by Glamsterdam gas repricing that requires an input size limit change. Other repriced precompiles (KZG Point Evaluation, BLS12-381 G1Add, BLS12-381 G2Add) have fixed-size inputs and do not require limit adjustments. See the [precompile gas cost risk analysis](https://github.com/ethereum-optimism/optimism/issues/20195) and the [Karst exec-engine spec](https://specs.optimism.io/protocol/karst/exec-engine.html) for details.
</Info>

### `CANNON_KONA` as respected game type: withdrawal proving changes

After Upgrade 19, the respected game type changes from `CANNON` (0) to `CANNON_KONA` (8). Withdrawals must be proven against `CANNON_KONA` games.

* **For users proving withdrawals:** no action required, the `OptimismPortal` will automatically recognize `CANNON_KONA` as the respected game type after the upgrade.
* **For chain operators running permissionless fault proofs:** `op-challenger` must be configured to support `cannon-kona` trace types **prior** to the upgrade. Failure to update `op-challenger` may result in uncontested invalid games.

### L2CM: new L2 predeploy upgrade mechanism

L2CM introduces a new mechanism for upgrading L2 predeploy contracts. App developers building on OP Stack chains should be aware that predeploy contracts may now be upgraded through L2CM transactions. If your application interacts directly with predeploy contracts, review the changelog for each upgrade to understand new capabilities.

## For node operators: hard fork preparation

Because Upgrade 19 includes hard fork changes (Osaka on L2 and Glamsterdam Defense), all node operators must update their execution and consensus clients before the activation timestamp.

<Steps>
  <Step title="Update to the latest release">
    Update `op-node` and `op-reth` to the version specified in the component table below. The new version includes consensus rules for the Karst hard fork activation for chains in the superchain-registry.
  </Step>

  <Step title="Configure the Karst activation timestamp">
    How you configure the Karst activation timestamp depends on whether your chain is in the superchain-registry.

    **Chains in the superchain-registry** (e.g. `Soneium`, `Ink`, `Unichain`, `Mode`, `Metal`, `Zora`):
    The Karst activation timestamp is built into the updated releases — no manual timestamp configuration is needed. Make sure you are using the network flag so that op-node and op-reth load chain config from the superchain-registry:

    * `op-node`: [`--network <network>`](/node-operators/reference/op-node-config#network) (e.g. `--network soneium-mainnet`, `--network op-sepolia`)
    * `op-reth`: [`--chain <network>`](/node-operators/reference/op-reth-config#chain) (e.g. `--chain soneium`, `--chain unichain`)

    **Chains not in the superchain-registry:**
    Set the activation timestamp manually in each client's config:

    * `op-node`: set `karst_time: <timestamp>` in the rollup config
    * `op-reth`: set `"karstTime": <timestamp>` in the genesis file
  </Step>

  <Step title="Verify activation configuration">
    Make the following checks to verify that your node is properly configured:

    * The configurations will be logged at startup
    * Check that the Karst time is set to its correct activation timestamp
  </Step>
</Steps>

## For chain operators

Update the following components:

| Component        | Version   | Notes                                                                                                                                                                                                 |
| ---------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `op-node`        | `v1.19.1` | Must be updated **prior** to the activation timestamp. Includes the Karst gas limit fix (`keep_karst_upgrade_gas`). Replaces `v1.19.0`, which is now deprecated.                                      |
| `op-reth`        | `v2.3.3`  | Must be updated **prior** to the activation timestamp. Fixes SCR loading for `op-sepolia` and `op-mainnet`. Adds `engine_getPayloadV5` support for Karst. Replaces `v2.3.2`, which is now deprecated. |
| `op-challenger`  | `v1.9.3`  | *(unchanged)* Must be updated **prior** to the upgrade.                                                                                                                                               |
| `op-dispute-mon` | `v1.5.2`  | *(unchanged)* Must be updated **prior** to the upgrade.                                                                                                                                               |
| `op-contracts`   | `v7.0.0`  | *(unchanged)*                                                                                                                                                                                         |
| `rollup-boost`   | `v0.7.16` | Only if running Flashblocks. Adds `engine_getPayloadV5` support for Karst. Replaces `v0.7.15`.                                                                                                        |
| `op-rbuilder`    | `v0.4.9`  | Only if running Flashblocks. Fixes SCR loading for `op-sepolia`/`op-mainnet`. Adds `engine_getPayloadV5` support. Replaces `v0.4.8`.                                                                  |

<Warning>
  **Upgrade order:** On networks that have **not yet activated Karst** (pre-Karst), `op-node`, `op-reth`, and `op-rbuilder` can be upgraded independently in any order — they will continue using the previous Engine API version until the fork timestamp. On networks that have **already activated Karst** (post-Karst, e.g. Sepolia chains), all three must be upgraded together in a coordinated rollout, as Karst requires `engine_getPayloadV5` and a version mismatch will cause downtime.
</Warning>

### Gas limit: action required for affected chains

* If your chain has `keep_karst_upgrade_gas = true`, the elevated gas limit is enshrined and you must restore your intended limit with a `SystemConfig.setGasLimit()` transaction after activation (see below).
* If your chain does not have the flag, the latest releases mean you are not affected and no action is required.
* If your chain already activated Karst, it **must** use `keep_karst_upgrade_gas = true`. Some chains that have not yet activated Karst (including mainnet chains) also have the flag set — check the list below to see whether yours is affected.

The chains with `keep_karst_upgrade_gas = true` (the [canonical list](https://github.com/ethereum-optimism/superchain-registry/pull/1259/files)) are:

* **Mainnet:** `OP`, `Ink`, `Metal`, `Mode`, `Soneium`, `Unichain`, `Zora`
* **Sepolia:** `OP`, `Ink`, `Lisk`, `Metal`, `Mode`, `Soneium (Minato)`, `Unichain`, `Zora`

Sepolia chains have already activated Karst, so the action is due now.

#### Restoring the gas limit

After activation, submit a `setGasLimit()` transaction from your chain's `SystemConfig` owner to set the block gas limit back to your intended value.

<Steps>
  <Step title="Read the current (elevated) gas limit">
    ```bash theme={null}
    cast call <SYSTEM_CONFIG_ADDRESS> "gasLimit()(uint64)" --rpc-url <L1_RPC_URL>
    ```
  </Step>

  <Step title="Submit setGasLimit() from the SystemConfig owner">
    The transaction must be sent by the `SystemConfig` owner (typically a multisig). Set `<INTENDED_GAS_LIMIT>` to the value your chain ran with before Karst activation.

    ```bash theme={null}
    cast send <SYSTEM_CONFIG_ADDRESS> "setGasLimit(uint64)" <INTENDED_GAS_LIMIT> \
      --rpc-url <L1_RPC_URL> --private-key <SYSTEM_CONFIG_OWNER_KEY>
    ```

    If the owner is a Safe multisig, build and execute the call through your normal multisig flow instead of a direct `cast send`.
  </Step>

  <Step title="Verify the new gas limit">
    Re-read `gasLimit()` on the `SystemConfig` and confirm it matches your intended value. The change takes effect on L2 once the corresponding `ConfigUpdate` is derived.
  </Step>
</Steps>

### For permissioned fault proof chains

Chains running **permissioned** fault proofs (game type `PERMISSIONED`) do not need the full permissionless setup below, but `op-challenger` still requires a cannon prestate to be configured at startup even though the legacy `CANNON` (0) prestate is not used after the upgrade.

If you are currently passing `--prestates-url=<YOUR_PRESTATE_URL>`, replace it with a placeholder cannon prestate:

```bash theme={null}
--cannon-prestate=any-string
# or
OP_CHALLENGER_CANNON_PRESTATE=any-string
```

<Info>
  This value is a placeholder only — permissioned chains do not generate `CANNON` (0) trace data, so the prestate is never loaded. Your `cannon-kona` prestate configuration is unaffected.
</Info>

### For permissionless fault proof enabled chains

With `CANNON_KONA` becoming the respected game type, chains running permissionless fault proofs must ensure their off-chain infrastructure is fully Kona-ready. This is critical — after the upgrade, withdrawals are proven against `CANNON_KONA` games.

<Info>
  Optimism will complete the onchain upgrade for chains managed by the Optimism Security Council.
  However, off-chain components must still be configured by operators to support cannon-kona games.
  If you are a permissionless FP enabled chain not included in the prepared set, you must perform all steps below yourself.
</Info>

If your chain was already configured for `cannon-kona` as part of [Upgrade 18](/notices/archive/upgrade-18), verify that your `op-challenger` is running the latest version and that prestates are up to date.

<Steps>
  <Step title="Verify the new kona-client absolute prestate">
    The absolute prestate is generated with [`kona-client/v1.6.0-rc.2`](https://github.com/ethereum-optimism/optimism/releases/tag/kona-client%2Fv1.6.0-rc.2). As of Upgrade 19, the `kona-client` source lives inside the [`ethereum-optimism/optimism`](https://github.com/ethereum-optimism/optimism) monorepo.

    Choose the verification path that matches your chain:

    <Tabs>
      <Tab title="Chains in the superchain-registry">
        Two variants ship with this release:

        * **`cannon64-kona`** (`0x0337ecb3604c0b40c352e0c7711beb17a212d583f4fe956fd8d66e29ad5f9025`) — standard variant for chains adopting `CANNON_KONA` (game type 8) as the respected game type.
        * **`cannon64-kona-interop`** (`0x03e3a42cf9a1d116f414206c465c6cdb74556136090e7c9556329403da0f310f`) — for chains also running the interop fault proof variant.

        These prestates apply to the following chains:

        * Mainnet and Sepolia: `OP`, `Ink`, `Soneium`, and `Unichain`

        You can verify both absolute prestates by running the following command in the root of the monorepo on the `kona-client/v1.6.0-rc.2` tag:

        ```shell theme={null}
        just reproducible-prestate-kona
        ```

        Then extract each hash with:

        ```shell theme={null}
        jq -r .pre rust/kona/prestate-artifacts-cannon/prestate-proof.json          # cannon64-kona
        jq -r .pre rust/kona/prestate-artifacts-cannon-interop/prestate-proof.json  # cannon64-kona-interop
        ```

        Verify that your target prestate was calculated as expected and matches the corresponding entry in
        [standard-prestates.toml](https://github.com/ethereum-optimism/superchain-registry/blob/main/validation/standard/standard-prestates.toml).
      </Tab>

      <Tab title="Chains with custom chain configurations">
        If you generated a custom prestate following the [Custom kona-client prestate tutorial](/chain-operators/tutorials/kona-custom-prestate), the resulting hash is specific to your chain and will not appear in `standard-prestates.toml`. Verify it with these three checks:

        1. **Confirm the inputs.** Open the rollup config files inside the `KONA_CUSTOM_CONFIGS_DIR` you used during the build and confirm the chain ID, fork timestamps (including `karst_time` once it lands), and L1/L2 contract addresses match your chain's actual state. The prestate is only as correct as the configs you fed into the build — once the inputs are right, the build is deterministic.

        2. **Confirm your chain is embedded (pre-deployment).** A custom build that silently fails to merge your chain produces the *standard* prestate hash. Confirm your chain is actually inside the prestate image before you deploy anything:

           ```bash theme={null}
           gunzip -c rust/kona/prestate-artifacts-cannon/prestate.bin.gz | strings | grep "<your-chain-name>"
           ```

           Run it once per chain in your `chainList.json`; every chain must match at least once.

        3. **On-chain match (post-deployment).** After the new game type is registered, confirm the on-chain absolute prestate matches `jq -r .pre rust/kona/prestate-artifacts-cannon/prestate-proof.json`. On current (v2.4+) contracts the prestate lives in the `DisputeGameFactory`'s `gameArgs` (the first 32 bytes of `gameArgs(<gameType>)`), so read it there or from a *created game instance* — `absolutePrestate()` on the bare implementation returns zero because the value is a clone-with-immutable-args argument, not a constructor immutable.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Upload your new preimage files">
    Upload the new preimage file to where you're storing your other absolute preimage files. This should be the location where you're pointing your `--prestates-url` at. The `op-challenger` will fetch this file and use it when it needs to challenge games.

    Rename the file to have the absolute prestate hash as the filename (e.g., `0x<hash>.bin.gz`).
  </Step>

  <Step title="Configure op-challenger for cannon-kona as respected game type">
    Ensure `op-challenger` is updated and configured to support Kona games.
    Include `cannon-kona` in trace types:

    ```bash theme={null}
    OP_CHALLENGER_TRACE_TYPE="cannon,cannon-kona,permissioned"
    # or
    --trace-type=cannon,cannon-kona,permissioned
    ```

    Upload new preimage files and ensure they're available for the challenger.
    If both preimage files are uploaded at the same location:

    ```bash theme={null}
    --prestates-url=<PRESTATES_URL>
    ```

    If stored separately:

    ```bash theme={null}
    --cannon-prestates-url=<CANNON_PRESTATES_URL>
    --cannon-kona-prestates-url=<CANNON_KONA_PRESTATES_URL>
    ```

    If not using the standard OP Labs `op-challenger` Docker image, set the `kona-host` binary path:

    ```bash theme={null}
    OP_CHALLENGER_CANNON_KONA_SERVER=/path/to/kona-host
    # or
    --cannon-kona-server=/path/to/kona-host
    ```

    Build `kona-host` from the same release as the configured cannon-kona prestate.
  </Step>

  <Step title="Execute the L1 Contract Upgrade">
    Once your infrastructure is ready, execute the upgrade transaction. This should be done by making a delegatecall to the `upgrade()` function of the OP Contracts Manager (OPCMv2) at the address that will be listed in [the registry](https://github.com/ethereum-optimism/superchain-registry/blob/main/validation/standard/standard-versions-mainnet.toml).

    Please simulate and validate the expected output prior to executing the transaction.
  </Step>

  <Step title="Update op-proposer to use CANNON_KONA game type">
    <Warning>
      **`op-proposer` will start failing immediately after the contract upgrade executes** if the game type is not updated. The respected game type changes to `CANNON_KONA` (8) on-chain at the moment the upgrade transaction lands — any proposals submitted against the old game type (0) after that point will be rejected. Update the game type before or at the same time as the contract upgrade.
    </Warning>

    After the L1 contract upgrade executes, the respected game type changes from `CANNON` (0) to `CANNON_KONA` (8) on-chain. `op-proposer` must be reconfigured to submit proposals against the new game type, or it will begin failing immediately after the upgrade.

    Set the game type to `8` in your proposer configuration:

    ```bash theme={null}
    OP_PROPOSER_GAME_TYPE=8
    # or
    --game-type=8
    ```

    Update this before or immediately after the contract upgrade executes to avoid a gap in proposal submissions.
  </Step>
</Steps>

## For app developers

* **EIP-7825 transaction gas limit (Osaka on L2):** L2 transactions are now subject to a per-transaction gas limit of 2^24 = 16,777,216 gas. If your application submits transactions with very high gas limits, verify they remain within the new threshold. Deposit transactions are exempt. Most applications will not be affected.
* **MODEXP gas changes ([EIP-7883](https://eips.ethereum.org/EIPS/eip-7883) + [EIP-7823](https://eips.ethereum.org/EIPS/eip-7823), Osaka on L2):** the gas cost floor for `MODEXP` (`0x05`) increases, and a new input size cap (1024-byte modulus) is enforced. If your contracts call `MODEXP`, update your gas estimates and verify inputs are within the new size limit.
* **P256VERIFY gas change ([EIP-7951](https://eips.ethereum.org/EIPS/eip-7951), Osaka on L2):** the gas cost for `P256VERIFY` (`0x100`) increases from 3,450 to 6,900 gas. If your contracts call `P256VERIFY`, update your gas estimates.
* **CLZ opcode ([EIP-7939](https://eips.ethereum.org/EIPS/eip-7939), Osaka on L2):** a new Count Leading Zeros opcode (0x1e) is available. Existing contracts are not affected.
* **BN256 pairing input size limit (Glamsterdam Defense):** the maximum input for `ecPairing` (`0x08`) is reduced from 427 to 300 pairs (57,600 bytes). If your contracts call the BN256 pairing precompile with more than 300 pairs, those calls will fail. Standard ZK proof verification (e.g., Groth16) is not affected.
* **L2 predeploy upgrades:** if your application interacts directly with predeploy contracts (e.g., `L2ToL1MessagePasser`, `L2CrossDomainMessenger`, `L2StandardBridge`), review the changelog for new functions or behavior changes.
* **Withdrawal proving:** the respected game type changes from `CANNON` (0) to `CANNON_KONA` (8). Standard SDK usage handles this automatically.
