Repository: ethereum-optimism/optimism
Pull request #7227 (2023-09-13 14:02:17)

op-node: protocol versions signal handling
**Description**

Implements op-node protocol-version signal handling (see specs PR: https://github.com/ethereum-optimism/optimism/pull/6388 ):
- Metrics
- Logging
- Communication of protocol version to geth
- Protocol version loading as part of runtime config system
- Opt-in functionality to halt node on outdated config
- Update op-geth Go dependency ~~(TODO: this is a placeholder reference to the commit of the open PR for now)~~ Updated to latest op-geth at [`b84ba11915a0aac316172bc27f10c7e2287b922b`](https://github.com/ethereum-optimism/op-geth/commit/b84ba11915a0aac316172bc27f10c7e2287b922b)

Note: this PR builds on two other PRs:
- The `ProtocolVersions` contract: #7226 
- The op-geth version handling (defines types and utils that are reused here in op-node): https://github.com/ethereum-optimism/op-geth/pull/128


This also includes a minor fix to `eth_getProof`-verification: zero-storage is not present in the MPT, and thus the proof shows exclusion, and returns a nil result and a nil error. To verify, we need to not encode the nil as an empty RLP value, but rather check that the claimed storage value is zero.

**before merging this PR we should merge the above two PRs, and update the `go.mod` to reference the canonical op-geth commit**

New optional hidden beta flags:
- `--beta.rollup.halt`: to opt in to halting
- `--beta.rollup.load-protocol-versions`: to opt in to the new configuration. If not set, the protocol versions contract address configuration is dropped on startup, and not used when dropped.


**Tests**

- Version types/utils are unit-tested in op-geth
- Config updates and handling is tested in op-e2e, including halting.
  - Halting is tested more extensively in op-geth.
  - The op-e2e overhead is a bit too large to run a matrix of halting tests, so I left it at a recommended + required version-change test there.

**Metadata**

Fix CLI-4210

Fix CLI-4400

mergify[bot]: Hey @protolambda! This PR has merge conflicts. Please fix them before continuing review.

Patch:
diff --git a/.changeset/mighty-countries-drum.md b/.changeset/mighty-countries-drum.md
deleted file mode 100644
index a09a47b06..000000000
--- a/.changeset/mighty-countries-drum.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@eth-optimism/sdk': patch
----
-
-Adds Sepolia & OP Sepolia support to SDK
diff --git a/.changeset/smart-goats-camp.md b/.changeset/smart-goats-camp.md
deleted file mode 100644
index bfd7d0ee3..000000000
--- a/.changeset/smart-goats-camp.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@eth-optimism/contracts-bedrock': patch
----
-
-bumps sdk version to have access to sepolia deployments
diff --git a/.circleci/config.yml b/.circleci/config.yml
index aded89e04..a603a4b4a 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -887,14 +887,6 @@ jobs:
           name: Build
           command: make indexer
           working_directory: indexer
-      - run:
-          name: Install tygo
-          command: go install github.com/gzuidhof/tygo@latest
-          working_directory: indexer/api-ts
-      - run:
-          name: Check generated code
-          command: npm run generate && git diff --exit-code
-          working_directory: indexer/api-ts
 
   devnet:
     machine:
diff --git a/.github/workflows/release-docker-canary.yml b/.github/workflows/release-docker-canary.yml
index 43e75483f..b536f5a9c 100644
--- a/.github/workflows/release-docker-canary.yml
+++ b/.github/workflows/release-docker-canary.yml
@@ -56,7 +56,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -83,7 +83,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -110,7 +110,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -137,7 +137,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -164,7 +164,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -191,7 +191,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -229,7 +229,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2036ff069..60ea409c2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -81,7 +81,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -118,7 +118,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -145,7 +145,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -172,7 +172,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -199,7 +199,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -226,7 +226,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
@@ -253,7 +253,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
           password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
diff --git a/Makefile b/Makefile
index 2ec2d8712..451856c72 100644
--- a/Makefile
+++ b/Makefile
@@ -145,6 +145,7 @@ clean-node-modules:
 	rm -rf node_modules
 	rm -rf packages/**/node_modules
 
+
 tag-bedrock-go-modules:
 	./ops/scripts/tag-bedrock-go-modules.sh $(BEDROCK_TAGS_REMOTE) $(VERSION)
 .PHONY: tag-bedrock-go-modules
diff --git a/cannon/mipsevm/page.go b/cannon/mipsevm/page.go
index 5a1321582..8e14e7ca1 100644
--- a/cannon/mipsevm/page.go
+++ b/cannon/mipsevm/page.go
@@ -1,57 +1,18 @@
 package mipsevm
 
 import (
-	"bytes"
-	"compress/zlib"
-	"encoding/base64"
 	"encoding/hex"
-	"encoding/json"
 	"fmt"
-	"io"
-	"sync"
 
 	"github.com/ethereum/go-ethereum/crypto"
 )
 
-var zlibWriterPool = sync.Pool{
-	New: func() any {
-		var buf bytes.Buffer
-		return zlib.NewWriter(&buf)
-	},
-}
-
 type Page [PageSize]byte
 
-func (p *Page) MarshalJSON() ([]byte, error) { // nosemgrep
-	var out bytes.Buffer
-	w := zlibWriterPool.Get().(*zlib.Writer)
-	defer zlibWriterPool.Put(w)
-	w.Reset(&out)
-	if _, err := w.Write(p[:]); err != nil {
-		return nil, err
-	}
-	if err := w.Close(); err != nil {
-		return nil, err
-	}
-	return json.Marshal(out.Bytes())
-}
-
-func (p *Page) UnmarshalJSON(dat []byte) error {
-	// Strip off the `"` characters at the start & end.
-	dat = dat[1 : len(dat)-1]
-	// Decode b64 then decompress
-	r, err := zlib.NewReader(base64.NewDecoder(base64.StdEncoding, bytes.NewReader(dat)))
-	if err != nil {
-		return err
-	}
-	defer r.Close()
-	if n, err := r.Read(p[:]); n != PageSize {
-		return fmt.Errorf("epxeted %d bytes, but got %d", PageSize, n)
-	} else if err == io.EOF {
-		return nil
-	} else {
-		return err
-	}
+func (p *Page) MarshalText() ([]byte, error) {
+	dst := make([]byte, hex.EncodedLen(len(p)))
+	hex.Encode(dst, p[:])
+	return dst, nil
 }
 
 func (p *Page) UnmarshalText(dat []byte) error {
diff --git a/docs/fault-proof-alpha/manual.md b/docs/fault-proof-alpha/manual.md
index 2ab482462..570f1fa97 100644
--- a/docs/fault-proof-alpha/manual.md
+++ b/docs/fault-proof-alpha/manual.md
@@ -29,8 +29,7 @@ determine the root claim to use when creating the game. In simple cases, where t
 arbitrary hash can be used for claim values. For more advanced cases [cannon can be used](./cannon.md) to generate a
 trace, including the claim values to use at specific steps. Note that it is not valid to create a game that disputes an
 output root, using the final hash from a trace that confirms the output root is valid. To dispute an output root
-successfully, the trace must resolve that the disputed output root is invalid. This is indicated by the first byte of
-the claim value being set to the invalid [VM status](../../specs/cannon-fault-proof-vm.md#state-hash) (`0x01`).
+successfully, the trace must resolve that the disputed output root is invalid.
 
 The game can then be created by calling the `create` method on the `DisputeGameFactory` contract. This requires three
 parameters:
diff --git a/go.mod b/go.mod
index e3aaefd42..59d636897 100644
--- a/go.mod
+++ b/go.mod
@@ -208,6 +208,6 @@ require (
 	rsc.io/tmplfunc v0.0.3 // indirect
 )
 
-replace github.com/ethereum/go-ethereum v1.12.0 => github.com/ethereum-optimism/op-geth v1.101200.0-rc.1.0.20230818191139-f7376a28049b
+replace github.com/ethereum/go-ethereum v1.12.0 => github.com/ethereum-optimism/op-geth v1.101200.2-rc.1.0.20230914224024-b84ba11915a0
 
 //replace github.com/ethereum/go-ethereum v1.12.0 => ../go-ethereum
diff --git a/go.sum b/go.sum
index d7541519f..4cf869d04 100644
--- a/go.sum
+++ b/go.sum
@@ -162,8 +162,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
 github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
 github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
-github.com/ethereum-optimism/op-geth v1.101200.0-rc.1.0.20230818191139-f7376a28049b h1:YF2FE/QnbhvrHwDYJHnbTKgJvw2aKwB/dd7PO1zKNqY=
-github.com/ethereum-optimism/op-geth v1.101200.0-rc.1.0.20230818191139-f7376a28049b/go.mod h1:gRnPb21PoKcHm3kHqj9BQlQkwmhOGUvQoGEbC7z852Q=
+github.com/ethereum-optimism/op-geth v1.101200.2-rc.1.0.20230914224024-b84ba11915a0 h1:Qcu7OVMbKvbu7aaDC31OY0JCqFIr2N+/SGdBTnxukCs=
+github.com/ethereum-optimism/op-geth v1.101200.2-rc.1.0.20230914224024-b84ba11915a0/go.mod h1:gRnPb21PoKcHm3kHqj9BQlQkwmhOGUvQoGEbC7z852Q=
 github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20230817174831-5d3ca1966435 h1:2CzkJkkTLuVyoVFkoW5w6vDB2Q7eJzxXw/ybA17xjqM=
 github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20230817174831-5d3ca1966435/go.mod h1:v2YpePbdGBF0Gr6VWq49MFFmcTW0kRYZ2ingBJYWEwg=
 github.com/ethereum/c-kzg-4844 v0.2.0 h1:+cUvymlnoDDQgMInp25Bo3OmLajmmY8mLJ/tLjqd77Q=
diff --git a/indexer/api-ts/LICENSE b/indexer/api-ts/LICENSE
deleted file mode 100644
index 86541fdc7..000000000
--- a/indexer/api-ts/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2022 Optimism
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/indexer/api-ts/README.md b/indexer/api-ts/README.md
deleted file mode 100644
index 8cb1d1946..000000000
--- a/indexer/api-ts/README.md
+++ /dev/null
@@ -1,1 +0,0 @@
-Generated typescript types for https://github.com/ethereum-optimism/optimism/tree/develop/indexer
diff --git a/indexer/api-ts/generated.ts b/indexer/api-ts/generated.ts
deleted file mode 100644
index 7b0b1616d..000000000
--- a/indexer/api-ts/generated.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-// Code generated by tygo. DO NOT EDIT.
-
-//////////
-// source: deposits.go
-
-export interface DepositItem {
-  guid: string;
-  from: string;
-  to: string;
-  timestamp: number /* uint64 */;
-  L1TxHash: string;
-  L2TxHash: string;
-  Block: string;
-  amount: string;
-  l1Token: string;
-  l2Token: string;
-}
-export interface DepositResponse {
-  cursor: string;
-  hasNextPage: boolean;
-  items: DepositItem[];
-}
-
-//////////
-// source: routes.go
-
-export interface Routes {
-  Logger: any /* log.Logger */;
-  BridgeTransfersView: any /* database.BridgeTransfersView */;
-  Router?: any /* chi.Mux */;
-}
-
-//////////
-// source: withdrawals.go
-
-export interface WithdrawalItem {
-  guid: string;
-  from: string;
-  to: string;
-  transactionHash: string;
-  timestamp: number /* uint64 */;
-  l2BlockHash: string;
-  amount: string;
-  proof: string;
-  claim: string;
-  l1Token: string;
-  l2Token: string;
-}
-export interface WithdrawalResponse {
-  cursor: string;
-  hasNextPage: boolean;
-  items: WithdrawalItem[];
-}
diff --git a/indexer/api-ts/indexer.cjs b/indexer/api-ts/indexer.cjs
deleted file mode 100644
index 066549221..000000000
--- a/indexer/api-ts/indexer.cjs
+++ /dev/null
@@ -1,51 +0,0 @@
-"use strict";
-var __defProp = Object.defineProperty;
-var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
-var __getOwnPropNames = Object.getOwnPropertyNames;
-var __hasOwnProp = Object.prototype.hasOwnProperty;
-var __export = (target, all) => {
-  for (var name in all)
-    __defProp(target, name, { get: all[name], enumerable: true });
-};
-var __copyProps = (to, from, except, desc) => {
-  if (from && typeof from === "object" || typeof from === "function") {
-    for (let key of __getOwnPropNames(from))
-      if (!__hasOwnProp.call(to, key) && key !== except)
-        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
-  }
-  return to;
-};
-var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
-
-// indexer.ts
-var indexer_exports = {};
-__export(indexer_exports, {
-  depositEndpoint: () => depositEndpoint,
-  withdrawalEndoint: () => withdrawalEndoint
-});
-module.exports = __toCommonJS(indexer_exports);
-var createQueryString = ({ cursor, limit }) => {
-  if (cursor === void 0 && limit === void 0) {
-    return "";
-  }
-  const queries = [];
-  if (cursor) {
-    queries.push(`cursor=${cursor}`);
-  }
-  if (limit) {
-    queries.push(`limit=${limit}`);
-  }
-  return `?${queries.join("&")}`;
-};
-var depositEndpoint = ({ baseUrl = "", address, cursor, limit }) => {
-  return [baseUrl, "deposits", address, createQueryString({ cursor, limit })].join("/");
-};
-var withdrawalEndoint = ({ baseUrl = "", address, cursor, limit }) => {
-  return [baseUrl, "withdrawals", address, createQueryString({ cursor, limit })].join("/");
-};
-// Annotate the CommonJS export names for ESM import in node:
-0 && (module.exports = {
-  depositEndpoint,
-  withdrawalEndoint
-});
-//# sourceMappingURL=indexer.cjs.map
\ No newline at end of file
diff --git a/indexer/api-ts/indexer.cjs.map b/indexer/api-ts/indexer.cjs.map
deleted file mode 100644
index e3b29f27a..000000000
--- a/indexer/api-ts/indexer.cjs.map
+++ /dev/null
@@ -1,1 +0,0 @@
-{"version":3,"sources":["indexer.ts"],"sourcesContent":["export * from './generated'\n\ntype PaginationOptions = {\n  limit?: number\n  cursor?: string\n}\n\ntype Options = {\n  baseUrl?: string\n  address: `0x${string}`\n} & PaginationOptions\n\nconst createQueryString = ({ cursor, limit }: PaginationOptions): string => {\n  if (cursor === undefined && limit === undefined) {\n    return ''\n  }\n  const queries: string[] = []\n  if (cursor) {\n    queries.push(`cursor=${cursor}`)\n  }\n  if (limit) {\n    queries.push(`limit=${limit}`)\n  }\n  return `?${queries.join('&')}`\n}\n\nexport const depositEndpoint = ({ baseUrl = '', address, cursor, limit }: Options): string => {\n  return [baseUrl, 'deposits', address, createQueryString({ cursor, limit })].join('/')\n}\n\nexport const withdrawalEndoint = ({ baseUrl = '', address, cursor, limit }: Options): string => {\n  return [baseUrl, 'withdrawals', address, createQueryString({ cursor, limit })].join('/')\n}\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYA,IAAM,oBAAoB,CAAC,EAAE,QAAQ,MAAM,MAAiC;AAC1E,MAAI,WAAW,UAAa,UAAU,QAAW;AAC/C,WAAO;AAAA,EACT;AACA,QAAM,UAAoB,CAAC;AAC3B,MAAI,QAAQ;AACV,YAAQ,KAAK,UAAU,MAAM,EAAE;AAAA,EACjC;AACA,MAAI,OAAO;AACT,YAAQ,KAAK,SAAS,KAAK,EAAE;AAAA,EAC/B;AACA,SAAO,IAAI,QAAQ,KAAK,GAAG,CAAC;AAC9B;AAEO,IAAM,kBAAkB,CAAC,EAAE,UAAU,IAAI,SAAS,QAAQ,MAAM,MAAuB;AAC5F,SAAO,CAAC,SAAS,YAAY,SAAS,kBAAkB,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG;AACtF;AAEO,IAAM,oBAAoB,CAAC,EAAE,UAAU,IAAI,SAAS,QAAQ,MAAM,MAAuB;AAC9F,SAAO,CAAC,SAAS,eAAe,SAAS,kBAAkB,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG;AACzF;","names":[]}
\ No newline at end of file
diff --git a/indexer/api-ts/indexer.js b/indexer/api-ts/indexer.js
deleted file mode 100644
index b33fdd967..000000000
--- a/indexer/api-ts/indexer.js
+++ /dev/null
@@ -1,25 +0,0 @@
-// indexer.ts
-var createQueryString = ({ cursor, limit }) => {
-  if (cursor === void 0 && limit === void 0) {
-    return "";
-  }
-  const queries = [];
-  if (cursor) {
-    queries.push(`cursor=${cursor}`);
-  }
-  if (limit) {
-    queries.push(`limit=${limit}`);
-  }
-  return `?${queries.join("&")}`;
-};
-var depositEndpoint = ({ baseUrl = "", address, cursor, limit }) => {
-  return [baseUrl, "deposits", address, createQueryString({ cursor, limit })].join("/");
-};
-var withdrawalEndoint = ({ baseUrl = "", address, cursor, limit }) => {
-  return [baseUrl, "withdrawals", address, createQueryString({ cursor, limit })].join("/");
-};
-export {
-  depositEndpoint,
-  withdrawalEndoint
-};
-//# sourceMappingURL=indexer.js.map
\ No newline at end of file
diff --git a/indexer/api-ts/indexer.js.map b/indexer/api-ts/indexer.js.map
deleted file mode 100644
index 5be1e94a4..000000000
--- a/indexer/api-ts/indexer.js.map
+++ /dev/null
@@ -1,1 +0,0 @@
-{"version":3,"sources":["indexer.ts"],"sourcesContent":["export * from './generated'\n\ntype PaginationOptions = {\n  limit?: number\n  cursor?: string\n}\n\ntype Options = {\n  baseUrl?: string\n  address: `0x${string}`\n} & PaginationOptions\n\nconst createQueryString = ({ cursor, limit }: PaginationOptions): string => {\n  if (cursor === undefined && limit === undefined) {\n    return ''\n  }\n  const queries: string[] = []\n  if (cursor) {\n    queries.push(`cursor=${cursor}`)\n  }\n  if (limit) {\n    queries.push(`limit=${limit}`)\n  }\n  return `?${queries.join('&')}`\n}\n\nexport const depositEndpoint = ({ baseUrl = '', address, cursor, limit }: Options): string => {\n  return [baseUrl, 'deposits', address, createQueryString({ cursor, limit })].join('/')\n}\n\nexport const withdrawalEndoint = ({ baseUrl = '', address, cursor, limit }: Options): string => {\n  return [baseUrl, 'withdrawals', address, createQueryString({ cursor, limit })].join('/')\n}\n\n"],"mappings":";AAYA,IAAM,oBAAoB,CAAC,EAAE,QAAQ,MAAM,MAAiC;AAC1E,MAAI,WAAW,UAAa,UAAU,QAAW;AAC/C,WAAO;AAAA,EACT;AACA,QAAM,UAAoB,CAAC;AAC3B,MAAI,QAAQ;AACV,YAAQ,KAAK,UAAU,MAAM,EAAE;AAAA,EACjC;AACA,MAAI,OAAO;AACT,YAAQ,KAAK,SAAS,KAAK,EAAE;AAAA,EAC/B;AACA,SAAO,IAAI,QAAQ,KAAK,GAAG,CAAC;AAC9B;AAEO,IAAM,kBAAkB,CAAC,EAAE,UAAU,IAAI,SAAS,QAAQ,MAAM,MAAuB;AAC5F,SAAO,CAAC,SAAS,YAAY,SAAS,kBAAkB,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG;AACtF;AAEO,IAAM,oBAAoB,CAAC,EAAE,UAAU,IAAI,SAAS,QAAQ,MAAM,MAAuB;AAC9F,SAAO,CAAC,SAAS,eAAe,SAAS,kBAAkB,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG;AACzF;","names":[]}
\ No newline at end of file
diff --git a/indexer/api-ts/indexer.ts b/indexer/api-ts/indexer.ts
deleted file mode 100644
index ada1776e1..000000000
--- a/indexer/api-ts/indexer.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-export * from './generated'
-
-type PaginationOptions = {
-  limit?: number
-  cursor?: string
-}
-
-type Options = {
-  baseUrl?: string
-  address: `0x${string}`
-} & PaginationOptions
-
-const createQueryString = ({ cursor, limit }: PaginationOptions): string => {
-  if (cursor === undefined && limit === undefined) {
-    return ''
-  }
-  const queries: string[] = []
-  if (cursor) {
-    queries.push(`cursor=${cursor}`)
-  }
-  if (limit) {
-    queries.push(`limit=${limit}`)
-  }
-  return `?${queries.join('&')}`
-}
-
-export const depositEndpoint = ({ baseUrl = '', address, cursor, limit }: Options): string => {
-  return [baseUrl, 'deposits', address, createQueryString({ cursor, limit })].join('/')
-}
-
-export const withdrawalEndoint = ({ baseUrl = '', address, cursor, limit }: Options): string => {
-  return [baseUrl, 'withdrawals', address, createQueryString({ cursor, limit })].join('/')
-}
-
diff --git a/indexer/api-ts/package.json b/indexer/api-ts/package.json
deleted file mode 100644
index 83bd73441..000000000
--- a/indexer/api-ts/package.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-  "name": "@eth-optimism/indexer-api",
-  "version": "0.0.1",
-  "description": "[Optimism] typescript types for the indexer service",
-  "main": "indexer.cjs",
-  "module": "indexer.js",
-  "types": "indexer.ts",
-  "type": "module",
-  "files": [
-    "*.ts",
-    "*.ts",
-    "*.js",
-    "*.js.map",
-    "*.cjs",
-    "*.cjs.map",
-    "LICENSE"
-  ],
-  "scripts": {
-    "clean": "rm -rf generated.ts indexer.cjs indexer.js",
-    "generate": "npm run clean && tygo generate && mv ../api/routes/index.ts generated.ts && npx tsup"
-  },
-  "keywords": [
-    "optimism",
-    "ethereum",
-    "indexer"
-  ],
-  "homepage": "https://github.com/ethereum-optimism/optimism/tree/develop/indexer#readme",
-  "license": "MIT",
-  "author": "Optimism PBC",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/ethereum-optimism/optimism.git"
-  }}
diff --git a/indexer/api-ts/tsconfig.json b/indexer/api-ts/tsconfig.json
deleted file mode 100644
index e249a8d0b..000000000
--- a/indexer/api-ts/tsconfig.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "compilerOptions": {
-    "outDir": "dist",
-    "strict": true,
-    "skipLibCheck": true,
-    "module": "ESNext",
-    "moduleResolution": "bundler",
-    "jsx": "react",
-    "target": "ESNext",
-    "noEmit": true
-  },
-  "include": ["."]
-}
diff --git a/indexer/api-ts/tsup.config.ts b/indexer/api-ts/tsup.config.ts
deleted file mode 100644
index 793f4d657..000000000
--- a/indexer/api-ts/tsup.config.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import packageJson from './package.json'
-
-export default {
-  name: packageJson.name,
-  entry: ['indexer.ts'],
-  outDir: '.',
-  format: ['esm', 'cjs'],
-  splitting: false,
-  sourcemap: true,
-  clean: false,
-}
diff --git a/indexer/api-ts/tygo.yaml b/indexer/api-ts/tygo.yaml
deleted file mode 100644
index cb2454ece..000000000
--- a/indexer/api-ts/tygo.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-packages:
-  - path: "github.com/ethereum-optimism/optimism/indexer/api/routes"
diff --git a/indexer/api/routes/common.go b/indexer/api/routes/common.go
index 9766b1d97..f434e9c28 100644
--- a/indexer/api/routes/common.go
+++ b/indexer/api/routes/common.go
@@ -7,6 +7,32 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 )
 
+// lazily typing numbers fixme
+type Transaction struct {
+	Timestamp       uint64 `json:"timestamp"`
+	TransactionHash string `json:"transactionHash"`
+}
+
+type Block struct {
+	BlockNumber int64  `json:"number"`
+	BlockHash   string `json:"hash"`
+	// ParentBlockHash   string `json:"parentHash"`
+}
+
+type Extensions struct {
+	OptimismBridgeAddress string `json:"OptimismBridgeAddress"`
+}
+
+type TokenInfo struct {
+	// TODO lazily typing ints go through them all with fine tooth comb once api is up
+	ChainId    int        `json:"chainId"`
+	Address    string     `json:"address"`
+	Name       string     `json:"name"`
+	Symbol     string     `json:"symbol"`
+	Decimals   int        `json:"decimals"`
+	Extensions Extensions `json:"extensions"`
+}
+
 func jsonResponse(w http.ResponseWriter, logger log.Logger, data interface{}, statusCode int) {
 	w.Header().Set("Content-Type", "application/json")
 	jsonData, err := json.Marshal(data)
diff --git a/indexer/api/routes/deposits.go b/indexer/api/routes/deposits.go
index 20078655e..1a4d13a4f 100644
--- a/indexer/api/routes/deposits.go
+++ b/indexer/api/routes/deposits.go
@@ -10,16 +10,16 @@ import (
 )
 
 type DepositItem struct {
-	Guid           string `json:"guid"`
-	From           string `json:"from"`
-	To             string `json:"to"`
-	Timestamp      uint64 `json:"timestamp"`
-	L1TxHash       string `json:"L1TxHash"`
-	L2TxHash       string `json:"L2TxHash"`
-	L1BlockHash    string `json:"Block"`
-	Amount         string `json:"amount"`
-	L1TokenAddress string `json:"l1Token"`
-	L2TokenAddress string `json:"l2Token"`
+	Guid string `json:"guid"`
+	From string `json:"from"`
+	To   string `json:"to"`
+	// TODO could consider OriginTx to be more generic to handling L2 to L2 deposits
+	// this seems more clear today though
+	Tx      Transaction `json:"Tx"`
+	Block   Block       `json:"Block"`
+	Amount  string      `json:"amount"`
+	L1Token TokenInfo   `json:"l1Token"`
+	L2Token TokenInfo   `json:"l2Token"`
 }
 
 type DepositResponse struct {
@@ -28,22 +28,47 @@ type DepositResponse struct {
 	Items       []DepositItem `json:"items"`
 }
 
+// TODO this is original spec but maybe include the l2 block info too for the relayed tx
+// FIXME make a pure function that returns a struct instead of newWithdrawalResponse
 func newDepositResponse(deposits *database.L1BridgeDepositsResponse) DepositResponse {
 	items := make([]DepositItem, len(deposits.Deposits))
-	for i, deposit := range deposits.Deposits {
+	for _, deposit := range deposits.Deposits {
 		item := DepositItem{
-			Guid:           deposit.L1BridgeDeposit.TransactionSourceHash.String(),
-			L1BlockHash:    deposit.L1BlockHash.String(),
-			Timestamp:      deposit.L1BridgeDeposit.Tx.Timestamp,
-			L1TxHash:       deposit.L1TransactionHash.String(),
-			L2TxHash:       deposit.L2TransactionHash.String(),
-			From:           deposit.L1BridgeDeposit.Tx.FromAddress.String(),
-			To:             deposit.L1BridgeDeposit.Tx.ToAddress.String(),
-			Amount:         deposit.L1BridgeDeposit.Tx.Amount.String(),
-			L1TokenAddress: deposit.L1BridgeDeposit.TokenPair.LocalTokenAddress.String(),
-			L2TokenAddress: deposit.L1BridgeDeposit.TokenPair.RemoteTokenAddress.String(),
+			Guid: deposit.L1BridgeDeposit.TransactionSourceHash.String(),
+			Block: Block{
+				BlockNumber: 420420,  // TODO
+				BlockHash:   "0x420", // TODO
+
+			},
+			Tx: Transaction{
+				TransactionHash: "0x420", // TODO
+				Timestamp:       deposit.L1BridgeDeposit.Tx.Timestamp,
+			},
+			From:   deposit.L1BridgeDeposit.Tx.FromAddress.String(),
+			To:     deposit.L1BridgeDeposit.Tx.ToAddress.String(),
+			Amount: deposit.L1BridgeDeposit.Tx.Amount.String(),
+			L1Token: TokenInfo{
+				ChainId:  1,
+				Address:  deposit.L1BridgeDeposit.TokenPair.LocalTokenAddress.String(),
+				Name:     "TODO",
+				Symbol:   "TODO",
+				Decimals: 420,
+				Extensions: Extensions{
+					OptimismBridgeAddress: "0x420", // TODO
+				},
+			},
+			L2Token: TokenInfo{
+				ChainId:  10,
+				Address:  deposit.L1BridgeDeposit.TokenPair.RemoteTokenAddress.String(),
+				Name:     "TODO",
+				Symbol:   "TODO",
+				Decimals: 420,
+				Extensions: Extensions{
+					OptimismBridgeAddress: "0x420", // TODO
+				},
+			},
 		}
-		items[i] = item
+		items = append(items, item)
 	}
 
 	return DepositResponse{
diff --git a/indexer/api/routes/withdrawals.go b/indexer/api/routes/withdrawals.go
index 675299b8b..af3afd2d5 100644
--- a/indexer/api/routes/withdrawals.go
+++ b/indexer/api/routes/withdrawals.go
@@ -9,18 +9,31 @@ import (
 	"github.com/go-chi/chi/v5"
 )
 
+type Proof struct {
+	TransactionHash string `json:"transactionHash"`
+	BlockTimestamp  uint64 `json:"blockTimestamp"`
+	BlockNumber     int    `json:"blockNumber"`
+}
+
+type Claim struct {
+	TransactionHash string `json:"transactionHash"`
+	BlockTimestamp  uint64 `json:"blockTimestamp"`
+	BlockNumber     int    `json:"blockNumber"`
+}
+
 type WithdrawalItem struct {
-	Guid                 string `json:"guid"`
-	From                 string `json:"from"`
-	To                   string `json:"to"`
-	TransactionHash      string `json:"transactionHash"`
-	Timestamp            uint64 `json:"timestamp"`
-	L2BlockHash          string `json:"l2BlockHash"`
-	Amount               string `json:"amount"`
-	ProofTransactionHash string `json:"proof"`
-	ClaimTransactionHash string `json:"claim"`
-	L1TokenAddress       string `json:"l1Token"`
-	L2TokenAddress       string `json:"l2Token"`
+	Guid            string      `json:"guid"`
+	Tx              Transaction `json:"Tx"`
+	Block           Block       `json:"Block"`
+	From            string      `json:"from"`
+	To              string      `json:"to"`
+	TransactionHash string      `json:"transactionHash"`
+	Amount          string      `json:"amount"`
+	Proof           Proof       `json:"proof"`
+	Claim           Claim       `json:"claim"`
+	WithdrawalState string      `json:"withdrawalState"`
+	L1Token         TokenInfo   `json:"l1Token"`
+	L2Token         TokenInfo   `json:"l2Token"`
 }
 
 type WithdrawalResponse struct {
@@ -32,20 +45,55 @@ type WithdrawalResponse struct {
 // FIXME make a pure function that returns a struct instead of newWithdrawalResponse
 func newWithdrawalResponse(withdrawals *database.L2BridgeWithdrawalsResponse) WithdrawalResponse {
 	items := make([]WithdrawalItem, len(withdrawals.Withdrawals))
-	for i, withdrawal := range withdrawals.Withdrawals {
+	for _, withdrawal := range withdrawals.Withdrawals {
 		item := WithdrawalItem{
-			Guid:                 withdrawal.L2BridgeWithdrawal.TransactionWithdrawalHash.String(),
-			L2BlockHash:          withdrawal.L2BlockHash.String(),
-			From:                 withdrawal.L2BridgeWithdrawal.Tx.FromAddress.String(),
-			To:                   withdrawal.L2BridgeWithdrawal.Tx.ToAddress.String(),
-			TransactionHash:      withdrawal.L2TransactionHash.String(),
-			Amount:               withdrawal.L2BridgeWithdrawal.Tx.Amount.String(),
-			ProofTransactionHash: withdrawal.ProvenL1TransactionHash.String(),
-			ClaimTransactionHash: withdrawal.FinalizedL1TransactionHash.String(),
-			L1TokenAddress:       withdrawal.L2BridgeWithdrawal.TokenPair.RemoteTokenAddress.String(),
-			L2TokenAddress:       withdrawal.L2BridgeWithdrawal.TokenPair.LocalTokenAddress.String(),
+			Guid: withdrawal.L2BridgeWithdrawal.TransactionWithdrawalHash.String(),
+			Block: Block{
+				BlockNumber: 420420,  // TODO
+				BlockHash:   "0x420", // TODO
+
+			},
+			Tx: Transaction{
+				TransactionHash: "0x420", // TODO
+				Timestamp:       withdrawal.L2BridgeWithdrawal.Tx.Timestamp,
+			},
+			From:            withdrawal.L2BridgeWithdrawal.Tx.FromAddress.String(),
+			To:              withdrawal.L2BridgeWithdrawal.Tx.ToAddress.String(),
+			TransactionHash: withdrawal.L2TransactionHash.String(),
+			Amount:          withdrawal.L2BridgeWithdrawal.Tx.Amount.String(),
+			Proof: Proof{
+				TransactionHash: withdrawal.ProvenL1TransactionHash.String(),
+				BlockTimestamp:  withdrawal.L2BridgeWithdrawal.Tx.Timestamp,
+				BlockNumber:     420, // TODO Block struct instead
+			},
+			Claim: Claim{
+				TransactionHash: withdrawal.FinalizedL1TransactionHash.String(),
+				BlockTimestamp:  withdrawal.L2BridgeWithdrawal.Tx.Timestamp, // Using L2 timestamp for now, might need adjustment
+				BlockNumber:     420,                                        // TODO block struct
+			},
+			WithdrawalState: "COMPLETE", // TODO
+			L1Token: TokenInfo{
+				ChainId:  1,
+				Address:  withdrawal.L2BridgeWithdrawal.TokenPair.RemoteTokenAddress.String(),
+				Name:     "Example", // TODO
+				Symbol:   "EXAMPLE", // TODO
+				Decimals: 18,        // TODO
+				Extensions: Extensions{
+					OptimismBridgeAddress: "0x636Af16bf2f682dD3109e60102b8E1A089FedAa8",
+				},
+			},
+			L2Token: TokenInfo{
+				ChainId:  10,
+				Address:  withdrawal.L2BridgeWithdrawal.TokenPair.LocalTokenAddress.String(),
+				Name:     "Example", // TODO
+				Symbol:   "EXAMPLE", // TODO
+				Decimals: 18,        // TODO
+				Extensions: Extensions{
+					OptimismBridgeAddress: "0x36Af16bf2f682dD3109e60102b8E1A089FedAa86",
+				},
+			},
 		}
-		items[i] = item
+		items = append(items, item)
 	}
 
 	return WithdrawalResponse{
diff --git a/indexer/config/config.go b/indexer/config/config.go
index d370c0eda..69eb82284 100644
--- a/indexer/config/config.go
+++ b/indexer/config/config.go
@@ -6,7 +6,6 @@ import (
 	"reflect"
 
 	"github.com/BurntSushi/toml"
-	"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/log"
 )
@@ -17,7 +16,7 @@ const (
 	defaultHeaderBufferSize = 500
 )
 
-// In the future, presets can just be onchain config and fetched on initialization
+// in future presets can just be onchain config and fetched on initialization
 
 // Config represents the `indexer.toml` file used to configure the indexer
 type Config struct {
@@ -28,80 +27,50 @@ type Config struct {
 	MetricsServer ServerConfig `toml:"metrics"`
 }
 
-// L1Contracts configures deployed contracts
+// fetch this via onchain config from RPCsConfig and remove from config in future
 type L1Contracts struct {
-	// administrative
-	AddressManager    common.Address `toml:"address-manager"`
-	SystemConfigProxy common.Address `toml:"system-config"`
-
-	// rollup state
-	OptimismPortalProxy common.Address `toml:"optimism-portal"`
-	L2OutputOracleProxy common.Address `toml:"l2-output-oracle"`
-
-	// bridging
+	OptimismPortalProxy         common.Address `toml:"optimism-portal"`
+	L2OutputOracleProxy         common.Address `toml:"l2-output-oracle"`
 	L1CrossDomainMessengerProxy common.Address `toml:"l1-cross-domain-messenger"`
 	L1StandardBridgeProxy       common.Address `toml:"l1-standard-bridge"`
-	L1ERC721BridgeProxy         common.Address `toml:"l1-erc721-bridge"`
-
-	// IGNORE: legacy contracts (only settable via presets)
-	LegacyCanonicalTransactionChain common.Address `toml:"-"`
-	LegacyStateCommitmentChain      common.Address `toml:"-"`
-}
 
-func (c L1Contracts) ForEach(cb func(string, common.Address) error) error {
-	contracts := reflect.ValueOf(c)
-	fields := reflect.VisibleFields(reflect.TypeOf(c))
-	for _, field := range fields {
-		// ruleid: unsafe-reflect-by-name
-		addr := (contracts.FieldByName(field.Name).Interface()).(common.Address)
-		if err := cb(field.Name, addr); err != nil {
-			return err
-		}
-	}
+	// Pre-Bedrock Legacy Contracts
+	LegacyCanonicalTransactionChain common.Address `toml:"l1-canonical-transaction-chain"`
 
-	return nil
-}
+	// Some more contracts -- L1ERC721Bridge, ProxyAdmin, SystemConfig, etc
+	// Ignore the auxiliary contracts?
 
-// L2Contracts configures core predeploy contracts. We explicitly specify
-// fields until we can detect and backfill new addresses
-type L2Contracts struct {
-	L2ToL1MessagePasser    common.Address
-	L2CrossDomainMessenger common.Address
-	L2StandardBridge       common.Address
-	L2ERC721Bridge         common.Address
+	// Legacy contracts? We'll add this in to index the legacy chain.
+	// Remove afterwards?
 }
 
-func L2ContractsFromPredeploys() L2Contracts {
-	return L2Contracts{
-		L2ToL1MessagePasser:    predeploys.L2ToL1MessagePasserAddr,
-		L2CrossDomainMessenger: predeploys.L2CrossDomainMessengerAddr,
-		L2StandardBridge:       predeploys.L2StandardBridgeAddr,
-		L2ERC721Bridge:         predeploys.L2ERC721BridgeAddr,
-	}
-}
-
-func (c L2Contracts) ForEach(cb func(string, common.Address) error) error {
-	contracts := reflect.ValueOf(c)
-	fields := reflect.VisibleFields(reflect.TypeOf(c))
-	for _, field := range fields {
+// converts struct of to a slice of addresses for easy iteration
+// also validates that all fields are addresses
+func (c *L1Contracts) AsSlice() ([]common.Address, error) {
+	clone := *c
+	contractValue := reflect.ValueOf(clone)
+	fields := reflect.VisibleFields(reflect.TypeOf(clone))
+	l1Contracts := make([]common.Address, len(fields))
+	for i, field := range fields {
 		// ruleid: unsafe-reflect-by-name
-		addr := (contracts.FieldByName(field.Name).Interface()).(common.Address)
-		if err := cb(field.Name, addr); err != nil {
-			return err
+		addr, ok := (contractValue.FieldByName(field.Name).Interface()).(common.Address)
+		if !ok {
+			return nil, fmt.Errorf("non-address found in L1Contracts: %s", field.Name)
 		}
+
+		l1Contracts[i] = addr
 	}
 
-	return nil
+	return l1Contracts, nil
 }
 
 // ChainConfig configures of the chain being indexed
 type ChainConfig struct {
 	// Configure known chains with the l2 chain id
-	Preset           int
-	L1StartingHeight uint `toml:"l1-starting-height"`
+	Preset int
 
-	L1Contracts L1Contracts `toml:"l1-contracts"`
-	L2Contracts L2Contracts `toml:"-"`
+	L1Contracts      L1Contracts `toml:"l1-contracts"`
+	L1StartingHeight uint        `toml:"l1-starting-height"`
 
 	// Bedrock starting heights only applicable for OP-Mainnet & OP-Goerli
 	L1BedrockStartingHeight uint `toml:"-"`
@@ -158,24 +127,18 @@ func LoadConfig(log log.Logger, path string) (Config, error) {
 	}
 
 	if conf.Chain.Preset != 0 {
-		preset, ok := Presets[conf.Chain.Preset]
+		knownPreset, ok := presetConfigs[conf.Chain.Preset]
 		if !ok {
 			return conf, fmt.Errorf("unknown preset: %d", conf.Chain.Preset)
 		}
 
-		log.Info("detected preset", "preset", conf.Chain.Preset, "name", preset.Name)
-		log.Info("setting L1 information from preset")
-		conf.Chain.L1Contracts = preset.ChainConfig.L1Contracts
-		conf.Chain.L1StartingHeight = preset.ChainConfig.L1StartingHeight
-		conf.Chain.L1BedrockStartingHeight = preset.ChainConfig.L1BedrockStartingHeight
-		conf.Chain.L2BedrockStartingHeight = preset.ChainConfig.L1BedrockStartingHeight
+		conf.Chain.L1Contracts = knownPreset.L1Contracts
+		conf.Chain.L1StartingHeight = knownPreset.L1StartingHeight
+		conf.Chain.L1BedrockStartingHeight = knownPreset.L1BedrockStartingHeight
+		conf.Chain.L2BedrockStartingHeight = knownPreset.L1BedrockStartingHeight
 	}
 
-	// Setup L2Contracts from predeploys
-	conf.Chain.L2Contracts = L2ContractsFromPredeploys()
-
-	// Setup defaults for some unset options
-
+	// Set polling defaults if not set
 	if conf.Chain.L1PollingInterval == 0 {
 		log.Info("setting default L1 polling interval", "interval", defaultLoopInterval)
 		conf.Chain.L1PollingInterval = defaultLoopInterval
diff --git a/indexer/config/presets.go b/indexer/config/presets.go
index a455b8e85..c4acec6fd 100644
--- a/indexer/config/presets.go
+++ b/indexer/config/presets.go
@@ -4,115 +4,73 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 )
 
-type Preset struct {
-	Name        string
-	ChainConfig ChainConfig
-}
-
-// In the future, presets can just be onchain config and fetched on initialization
-
-// Mapping of L2 chain ids to their preset chain configurations
-var Presets = map[int]Preset{
+// in future presets can just be onchain config and fetched on initialization
+// Mapping of l2 chain ids to their preset chain configurations
+var presetConfigs = map[int]ChainConfig{
+	// OP Mainnet
 	10: {
-		Name: "Optimism",
-		ChainConfig: ChainConfig{
-			L1Contracts: L1Contracts{
-				AddressManager:              common.HexToAddress("0xdE1FCfB0851916CA5101820A69b13a4E276bd81F"),
-				SystemConfigProxy:           common.HexToAddress("0x229047fed2591dbec1eF1118d64F7aF3dB9EB290"),
-				OptimismPortalProxy:         common.HexToAddress("0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"),
-				L2OutputOracleProxy:         common.HexToAddress("0xdfe97868233d1aa22e815a266982f2cf17685a27"),
-				L1CrossDomainMessengerProxy: common.HexToAddress("0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"),
-				L1StandardBridgeProxy:       common.HexToAddress("0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"),
-				L1ERC721BridgeProxy:         common.HexToAddress("0x5a7749f83b81B301cAb5f48EB8516B986DAef23D"),
-
-				// pre-bedrock
-				LegacyCanonicalTransactionChain: common.HexToAddress("0x5e4e65926ba27467555eb562121fac00d24e9dd2"),
-				LegacyStateCommitmentChain:      common.HexToAddress("0xBe5dAb4A2e9cd0F27300dB4aB94BeE3A233AEB19"),
-			},
-			L1StartingHeight:        13596466,
-			L1BedrockStartingHeight: 17422590,
-			L2BedrockStartingHeight: 105235063,
+		L1Contracts: L1Contracts{
+			OptimismPortalProxy:             common.HexToAddress("0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"),
+			L2OutputOracleProxy:             common.HexToAddress("0xdfe97868233d1aa22e815a266982f2cf17685a27"),
+			L1CrossDomainMessengerProxy:     common.HexToAddress("0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"),
+			L1StandardBridgeProxy:           common.HexToAddress("0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"),
+			LegacyCanonicalTransactionChain: common.HexToAddress("0x5e4e65926ba27467555eb562121fac00d24e9dd2"),
 		},
+		L1StartingHeight:        13596466,
+		L1BedrockStartingHeight: 17422590,
+		L2BedrockStartingHeight: 105235063,
 	},
+	// OP Goerli
 	420: {
-		Name: "Optimism Goerli",
-		ChainConfig: ChainConfig{
-			L1Contracts: L1Contracts{
-				AddressManager:              common.HexToAddress("0xa6f73589243a6A7a9023b1Fa0651b1d89c177111"),
-				SystemConfigProxy:           common.HexToAddress("0xAe851f927Ee40dE99aaBb7461C00f9622ab91d60"),
-				OptimismPortalProxy:         common.HexToAddress("0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383"),
-				L2OutputOracleProxy:         common.HexToAddress("0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0"),
-				L1CrossDomainMessengerProxy: common.HexToAddress("0x5086d1eEF304eb5284A0f6720f79403b4e9bE294"),
-				L1StandardBridgeProxy:       common.HexToAddress("0x636Af16bf2f682dD3109e60102b8E1A089FedAa8"),
-				L1ERC721BridgeProxy:         common.HexToAddress("0x8DD330DdE8D9898d43b4dc840Da27A07dF91b3c9"),
-
-				// pre-bedrock
-				LegacyCanonicalTransactionChain: common.HexToAddress("0x607F755149cFEB3a14E1Dc3A4E2450Cde7dfb04D"),
-				LegacyStateCommitmentChain:      common.HexToAddress("0x9c945aC97Baf48cB784AbBB61399beB71aF7A378"),
-			},
-			L1StartingHeight:        7017096,
-			L1BedrockStartingHeight: 8300214,
-			L2BedrockStartingHeight: 4061224,
+		L1Contracts: L1Contracts{
+			OptimismPortalProxy:             common.HexToAddress("0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383"),
+			L2OutputOracleProxy:             common.HexToAddress("0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0"),
+			L1CrossDomainMessengerProxy:     common.HexToAddress("0x5086d1eEF304eb5284A0f6720f79403b4e9bE294"),
+			L1StandardBridgeProxy:           common.HexToAddress("0x636Af16bf2f682dD3109e60102b8E1A089FedAa8"),
+			LegacyCanonicalTransactionChain: common.HexToAddress("0x607F755149cFEB3a14E1Dc3A4E2450Cde7dfb04D"),
 		},
+		L1StartingHeight:        7017096,
+		L1BedrockStartingHeight: 8300214,
+		L2BedrockStartingHeight: 4061224,
 	},
+	// Base Mainnet
 	8453: {
-		Name: "Base",
-		ChainConfig: ChainConfig{
-			L1Contracts: L1Contracts{
-				AddressManager:              common.HexToAddress("0x8EfB6B5c4767B09Dc9AA6Af4eAA89F749522BaE2"),
-				SystemConfigProxy:           common.HexToAddress("0x73a79Fab69143498Ed3712e519A88a918e1f4072"),
-				OptimismPortalProxy:         common.HexToAddress("0x49048044D57e1C92A77f79988d21Fa8fAF74E97e"),
-				L2OutputOracleProxy:         common.HexToAddress("0x56315b90c40730925ec5485cf004d835058518A0"),
-				L1CrossDomainMessengerProxy: common.HexToAddress("0x866E82a600A1414e583f7F13623F1aC5d58b0Afa"),
-				L1StandardBridgeProxy:       common.HexToAddress("0x3154Cf16ccdb4C6d922629664174b904d80F2C35"),
-				L1ERC721BridgeProxy:         common.HexToAddress("0x608d94945A64503E642E6370Ec598e519a2C1E53"),
-			},
-			L1StartingHeight: 17481768,
+		L1Contracts: L1Contracts{
+			OptimismPortalProxy:         common.HexToAddress("0x49048044D57e1C92A77f79988d21Fa8fAF74E97e"),
+			L2OutputOracleProxy:         common.HexToAddress("0x56315b90c40730925ec5485cf004d835058518A0"),
+			L1CrossDomainMessengerProxy: common.HexToAddress("0x866E82a600A1414e583f7F13623F1aC5d58b0Afa"),
+			L1StandardBridgeProxy:       common.HexToAddress("0x3154Cf16ccdb4C6d922629664174b904d80F2C35"),
 		},
+		L1StartingHeight: 17481768,
 	},
+	// Base Goerli
 	84531: {
-		Name: "Base Goerli",
-		ChainConfig: ChainConfig{
-			L1Contracts: L1Contracts{
-				AddressManager:              common.HexToAddress("0x4Cf6b56b14c6CFcB72A75611080514F94624c54e"),
-				SystemConfigProxy:           common.HexToAddress("0xb15eea247eCE011C68a614e4a77AD648ff495bc1"),
-				OptimismPortalProxy:         common.HexToAddress("0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA"),
-				L2OutputOracleProxy:         common.HexToAddress("0x2A35891ff30313CcFa6CE88dcf3858bb075A2298"),
-				L1CrossDomainMessengerProxy: common.HexToAddress("0x8e5693140eA606bcEB98761d9beB1BC87383706D"),
-				L1StandardBridgeProxy:       common.HexToAddress("0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a"),
-				L1ERC721BridgeProxy:         common.HexToAddress("0x5E0c967457347D5175bF82E8CCCC6480FCD7e568"),
-			},
-			L1StartingHeight: 8410981,
+		L1Contracts: L1Contracts{
+			OptimismPortalProxy:         common.HexToAddress("0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA"),
+			L2OutputOracleProxy:         common.HexToAddress("0x2A35891ff30313CcFa6CE88dcf3858bb075A2298"),
+			L1CrossDomainMessengerProxy: common.HexToAddress("0x8e5693140eA606bcEB98761d9beB1BC87383706D"),
+			L1StandardBridgeProxy:       common.HexToAddress("0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a"),
 		},
+		L1StartingHeight: 8410981,
 	},
+	// Zora mainnet
 	7777777: {
-		Name: "Zora",
-		ChainConfig: ChainConfig{
-			L1Contracts: L1Contracts{
-				AddressManager:              common.HexToAddress("0xEF8115F2733fb2033a7c756402Fc1deaa56550Ef"),
-				SystemConfigProxy:           common.HexToAddress("0xA3cAB0126d5F504B071b81a3e8A2BBBF17930d86"),
-				OptimismPortalProxy:         common.HexToAddress("0x1a0ad011913A150f69f6A19DF447A0CfD9551054"),
-				L2OutputOracleProxy:         common.HexToAddress("0x9E6204F750cD866b299594e2aC9eA824E2e5f95c"),
-				L1CrossDomainMessengerProxy: common.HexToAddress("0xdC40a14d9abd6F410226f1E6de71aE03441ca506"),
-				L1StandardBridgeProxy:       common.HexToAddress("0x3e2Ea9B92B7E48A52296fD261dc26fd995284631"),
-				L1ERC721BridgeProxy:         common.HexToAddress("0x83A4521A3573Ca87f3a971B169C5A0E1d34481c3"),
-			},
-			L1StartingHeight: 17473923,
+		L1Contracts: L1Contracts{
+			OptimismPortalProxy:         common.HexToAddress("0x1a0ad011913A150f69f6A19DF447A0CfD9551054"),
+			L2OutputOracleProxy:         common.HexToAddress("0x9E6204F750cD866b299594e2aC9eA824E2e5f95c"),
+			L1CrossDomainMessengerProxy: common.HexToAddress("0xdC40a14d9abd6F410226f1E6de71aE03441ca506"),
+			L1StandardBridgeProxy:       common.HexToAddress("0x3e2Ea9B92B7E48A52296fD261dc26fd995284631"),
 		},
+		L1StartingHeight: 17473923,
 	},
+	// Zora goerli
 	999: {
-		Name: "Zora Goerli",
-		ChainConfig: ChainConfig{
-			L1Contracts: L1Contracts{
-				AddressManager:              common.HexToAddress("0x54f4676203dEDA6C08E0D40557A119c602bFA246"),
-				SystemConfigProxy:           common.HexToAddress("0xF66C9A5E4fE1A8a9bc44a4aF80505a4C3620Ee64"),
-				OptimismPortalProxy:         common.HexToAddress("0xDb9F51790365e7dc196e7D072728df39Be958ACe"),
-				L2OutputOracleProxy:         common.HexToAddress("0xdD292C9eEd00f6A32Ff5245d0BCd7f2a15f24e00"),
-				L1CrossDomainMessengerProxy: common.HexToAddress("0xD87342e16352D33170557A7dA1e5fB966a60FafC"),
-				L1StandardBridgeProxy:       common.HexToAddress("0x7CC09AC2452D6555d5e0C213Ab9E2d44eFbFc956"),
-				L1ERC721BridgeProxy:         common.HexToAddress("0x57C1C6b596ce90C0e010c358DD4Aa052404bB70F"),
-			},
-			L1StartingHeight: 8942381,
+		L1Contracts: L1Contracts{
+			OptimismPortalProxy:         common.HexToAddress("0xDb9F51790365e7dc196e7D072728df39Be958ACe"),
+			L2OutputOracleProxy:         common.HexToAddress("0xdD292C9eEd00f6A32Ff5245d0BCd7f2a15f24e00"),
+			L1CrossDomainMessengerProxy: common.HexToAddress("0xD87342e16352D33170557A7dA1e5fB966a60FafC"),
+			L1StandardBridgeProxy:       common.HexToAddress("0x7CC09AC2452D6555d5e0C213Ab9E2d44eFbFc956"),
 		},
+		L1StartingHeight: 8942381,
 	},
 }
diff --git a/indexer/database/blocks.go b/indexer/database/blocks.go
index a86fb8445..ef336491d 100644
--- a/indexer/database/blocks.go
+++ b/indexer/database/blocks.go
@@ -7,6 +7,8 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
 
+	"github.com/google/uuid"
+
 	"gorm.io/gorm"
 )
 
@@ -42,6 +44,25 @@ type L2BlockHeader struct {
 	BlockHeader `gorm:"embedded"`
 }
 
+type LegacyStateBatch struct {
+	// `default:0` is added since gorm would interepret 0 as NULL
+	// violating the primary key constraint.
+	Index uint64 `gorm:"primaryKey;default:0"`
+
+	Root                common.Hash `gorm:"serializer:bytes"`
+	Size                uint64
+	PrevTotal           uint64
+	L1ContractEventGUID uuid.UUID
+}
+
+type OutputProposal struct {
+	OutputRoot    common.Hash `gorm:"primaryKey;serializer:bytes"`
+	L2OutputIndex *big.Int    `gorm:"serializer:u256"`
+	L2BlockNumber *big.Int    `gorm:"serializer:u256"`
+
+	L1ContractEventGUID uuid.UUID
+}
+
 type BlocksView interface {
 	L1BlockHeader(common.Hash) (*L1BlockHeader, error)
 	L1BlockHeaderWithFilter(BlockHeader) (*L1BlockHeader, error)
@@ -51,6 +72,9 @@ type BlocksView interface {
 	L2BlockHeaderWithFilter(BlockHeader) (*L2BlockHeader, error)
 	L2LatestBlockHeader() (*L2BlockHeader, error)
 
+	LatestCheckpointedOutput() (*OutputProposal, error)
+	OutputProposal(index *big.Int) (*OutputProposal, error)
+
 	LatestEpoch() (*Epoch, error)
 }
 
@@ -59,6 +83,9 @@ type BlocksDB interface {
 
 	StoreL1BlockHeaders([]L1BlockHeader) error
 	StoreL2BlockHeaders([]L2BlockHeader) error
+
+	StoreLegacyStateBatches([]LegacyStateBatch) error
+	StoreOutputProposals([]OutputProposal) error
 }
 
 /**
@@ -80,6 +107,16 @@ func (db *blocksDB) StoreL1BlockHeaders(headers []L1BlockHeader) error {
 	return result.Error
 }
 
+func (db *blocksDB) StoreLegacyStateBatches(stateBatches []LegacyStateBatch) error {
+	result := db.gorm.CreateInBatches(stateBatches, batchInsertSize)
+	return result.Error
+}
+
+func (db *blocksDB) StoreOutputProposals(outputs []OutputProposal) error {
+	result := db.gorm.CreateInBatches(outputs, batchInsertSize)
+	return result.Error
+}
+
 func (db *blocksDB) L1BlockHeader(hash common.Hash) (*L1BlockHeader, error) {
 	return db.L1BlockHeaderWithFilter(BlockHeader{Hash: hash})
 }
@@ -111,6 +148,34 @@ func (db *blocksDB) L1LatestBlockHeader() (*L1BlockHeader, error) {
 	return &l1Header, nil
 }
 
+func (db *blocksDB) LatestCheckpointedOutput() (*OutputProposal, error) {
+	var outputProposal OutputProposal
+	result := db.gorm.Order("l2_output_index DESC").Take(&outputProposal)
+	if result.Error != nil {
+		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
+			return nil, nil
+		}
+
+		return nil, result.Error
+	}
+
+	return &outputProposal, nil
+}
+
+func (db *blocksDB) OutputProposal(index *big.Int) (*OutputProposal, error) {
+	var outputProposal OutputProposal
+	result := db.gorm.Where(&OutputProposal{L2OutputIndex: index}).Take(&outputProposal)
+	if result.Error != nil {
+		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
+			return nil, nil
+		}
+
+		return nil, result.Error
+	}
+
+	return &outputProposal, nil
+}
+
 // L2
 
 func (db *blocksDB) StoreL2BlockHeaders(headers []L2BlockHeader) error {
diff --git a/indexer/database/bridge_transfers.go b/indexer/database/bridge_transfers.go
index 54dcab3c3..44bacba8d 100644
--- a/indexer/database/bridge_transfers.go
+++ b/indexer/database/bridge_transfers.go
@@ -38,7 +38,6 @@ type L1BridgeDeposit struct {
 type L1BridgeDepositWithTransactionHashes struct {
 	L1BridgeDeposit L1BridgeDeposit `gorm:"embedded"`
 
-	L1BlockHash       common.Hash `gorm:"serializer:bytes"`
 	L1TransactionHash common.Hash `gorm:"serializer:bytes"`
 	L2TransactionHash common.Hash `gorm:"serializer:bytes"`
 }
@@ -51,7 +50,6 @@ type L2BridgeWithdrawal struct {
 type L2BridgeWithdrawalWithTransactionHashes struct {
 	L2BridgeWithdrawal L2BridgeWithdrawal `gorm:"embedded"`
 	L2TransactionHash  common.Hash        `gorm:"serializer:bytes"`
-	L2BlockHash        common.Hash        `gorm:"serializer:bytes"`
 
 	ProvenL1TransactionHash    common.Hash `gorm:"serializer:bytes"`
 	FinalizedL1TransactionHash common.Hash `gorm:"serializer:bytes"`
@@ -156,7 +154,7 @@ func (db *bridgeTransfersDB) L1BridgeDepositsByAddress(address common.Address, c
 	ethTransactionDeposits = ethTransactionDeposits.Joins("INNER JOIN l1_contract_events ON l1_contract_events.guid = initiated_l1_event_guid")
 	ethTransactionDeposits = ethTransactionDeposits.Select(`
 from_address, to_address, amount, data, source_hash AS transaction_source_hash,
-l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash, l1_contract_events.block_hash as l1_block_hash,
+l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash,
 l1_transaction_deposits.timestamp, NULL AS cross_domain_message_hash, ? AS local_token_address, ? AS remote_token_address`, ethAddressString, ethAddressString)
 	ethTransactionDeposits = ethTransactionDeposits.Order("timestamp DESC").Limit(limit + 1)
 	if cursorClause != "" {
@@ -168,7 +166,7 @@ l1_transaction_deposits.timestamp, NULL AS cross_domain_message_hash, ? AS local
 	depositsQuery = depositsQuery.Joins("INNER JOIN l1_contract_events ON l1_contract_events.guid = l1_transaction_deposits.initiated_l1_event_guid")
 	depositsQuery = depositsQuery.Select(`
 l1_bridge_deposits.from_address, l1_bridge_deposits.to_address, l1_bridge_deposits.amount, l1_bridge_deposits.data, transaction_source_hash,
-l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash, l1_contract_events.block_hash as l1_block_hash,
+l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash,
 l1_bridge_deposits.timestamp, cross_domain_message_hash, local_token_address, remote_token_address`)
 	depositsQuery = depositsQuery.Order("timestamp DESC").Limit(limit + 1)
 	if cursorClause != "" {
@@ -271,7 +269,7 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalsByAddress(address common.Address
 	ethTransactionWithdrawals = ethTransactionWithdrawals.Joins("LEFT JOIN l1_contract_events AS finalized_l1_events ON finalized_l1_events.guid = l2_transaction_withdrawals.finalized_l1_event_guid")
 	ethTransactionWithdrawals = ethTransactionWithdrawals.Select(`
 from_address, to_address, amount, data, withdrawal_hash AS transaction_withdrawal_hash,
-l2_contract_events.transaction_hash AS l2_transaction_hash, l2_contract_events.block_hash as l2_block_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash,
+l2_contract_events.transaction_hash AS l2_transaction_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash,
 l2_transaction_withdrawals.timestamp, NULL AS cross_domain_message_hash, ? AS local_token_address, ? AS remote_token_address`, ethAddressString, ethAddressString)
 	ethTransactionWithdrawals = ethTransactionWithdrawals.Order("timestamp DESC").Limit(limit + 1)
 	if cursorClause != "" {
@@ -285,7 +283,7 @@ l2_transaction_withdrawals.timestamp, NULL AS cross_domain_message_hash, ? AS lo
 	withdrawalsQuery = withdrawalsQuery.Joins("LEFT JOIN l1_contract_events AS finalized_l1_events ON finalized_l1_events.guid = l2_transaction_withdrawals.finalized_l1_event_guid")
 	withdrawalsQuery = withdrawalsQuery.Select(`
 l2_bridge_withdrawals.from_address, l2_bridge_withdrawals.to_address, l2_bridge_withdrawals.amount, l2_bridge_withdrawals.data, transaction_withdrawal_hash,
-l2_contract_events.transaction_hash AS l2_transaction_hash, l2_contract_events.block_hash as l2_block_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash,
+l2_contract_events.transaction_hash AS l2_transaction_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash,
 l2_bridge_withdrawals.timestamp, cross_domain_message_hash, local_token_address, remote_token_address`)
 	withdrawalsQuery = withdrawalsQuery.Order("timestamp DESC").Limit(limit + 1)
 	if cursorClause != "" {
diff --git a/indexer/database/mocks.go b/indexer/database/mocks.go
index a67a5fd9e..c03e60687 100644
--- a/indexer/database/mocks.go
+++ b/indexer/database/mocks.go
@@ -1,6 +1,8 @@
 package database
 
 import (
+	"math/big"
+
 	"github.com/ethereum/go-ethereum/common"
 
 	"github.com/stretchr/testify/mock"
@@ -51,6 +53,16 @@ func (m *MockBlocksView) L2LatestBlockHeader() (*L2BlockHeader, error) {
 	return args.Get(0).(*L2BlockHeader), args.Error(1)
 }
 
+func (m *MockBlocksView) LatestCheckpointedOutput() (*OutputProposal, error) {
+	args := m.Called()
+	return args.Get(0).(*OutputProposal), args.Error(1)
+}
+
+func (m *MockBlocksView) OutputProposal(index *big.Int) (*OutputProposal, error) {
+	args := m.Called()
+	return args.Get(0).(*OutputProposal), args.Error(1)
+}
+
 func (m *MockBlocksView) LatestEpoch() (*Epoch, error) {
 	args := m.Called()
 	return args.Get(0).(*Epoch), args.Error(1)
@@ -70,6 +82,15 @@ func (m *MockBlocksDB) StoreL2BlockHeaders(headers []L2BlockHeader) error {
 	return args.Error(1)
 }
 
+func (m *MockBlocksDB) StoreLegacyStateBatches(headers []LegacyStateBatch) error {
+	args := m.Called(headers)
+	return args.Error(1)
+}
+func (m *MockBlocksDB) StoreOutputProposals(headers []OutputProposal) error {
+	args := m.Called(headers)
+	return args.Error(1)
+}
+
 // MockDB is a mock database that can be used for testing
 type MockDB struct {
 	MockBlocks *MockBlocksDB
diff --git a/indexer/etl/l1_etl.go b/indexer/etl/l1_etl.go
index a4613928b..1ab44de6f 100644
--- a/indexer/etl/l1_etl.go
+++ b/indexer/etl/l1_etl.go
@@ -2,9 +2,7 @@ package etl
 
 import (
 	"context"
-	"errors"
 	"fmt"
-	"strings"
 	"sync"
 	"time"
 
@@ -12,7 +10,6 @@ import (
 	"github.com/ethereum-optimism/optimism/indexer/database"
 	"github.com/ethereum-optimism/optimism/indexer/node"
 	"github.com/ethereum-optimism/optimism/op-service/retry"
-	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/log"
 )
@@ -30,25 +27,12 @@ type L1ETL struct {
 func NewL1ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, client node.EthClient, contracts config.L1Contracts) (*L1ETL, error) {
 	log = log.New("etl", "l1")
 
-	zeroAddr := common.Address{}
-	l1Contracts := []common.Address{}
-	if err := contracts.ForEach(func(name string, addr common.Address) error {
-		// Since we dont have backfill support yet, we want to make sure all expected
-		// contracts are specified to ensure consistent behavior. Once backfill support
-		// is ready, we can relax this requirement.
-		if addr == zeroAddr && !strings.HasPrefix(name, "Legacy") {
-			log.Error("address not configured", "name", name)
-			return errors.New("all L1Contracts must be configured")
-		}
-
-		log.Info("configured contract", "name", name, "addr", addr)
-		l1Contracts = append(l1Contracts, addr)
-		return nil
-	}); err != nil {
+	latestHeader, err := db.Blocks.L1LatestBlockHeader()
+	if err != nil {
 		return nil, err
 	}
 
-	latestHeader, err := db.Blocks.L1LatestBlockHeader()
+	cSlice, err := contracts.AsSlice()
 	if err != nil {
 		return nil, err
 	}
@@ -58,6 +42,7 @@ func NewL1ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, cli
 	if latestHeader != nil {
 		log.Info("detected last indexed block", "number", latestHeader.Number, "hash", latestHeader.Hash)
 		fromHeader = latestHeader.RLPHeader.Header()
+
 	} else if cfg.StartHeight.BitLen() > 0 {
 		log.Info("no indexed state starting from supplied L1 height", "height", cfg.StartHeight.String())
 		header, err := client.BlockHeaderByNumber(cfg.StartHeight)
@@ -66,6 +51,7 @@ func NewL1ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, cli
 		}
 
 		fromHeader = header
+
 	} else {
 		log.Info("no indexed state, starting from genesis")
 	}
@@ -80,7 +66,7 @@ func NewL1ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, cli
 		log:             log,
 		metrics:         metrics,
 		headerTraversal: node.NewHeaderTraversal(client, fromHeader, cfg.ConfirmationDepth),
-		contracts:       l1Contracts,
+		contracts:       cSlice,
 		etlBatches:      etlBatches,
 
 		EthClient: client,
diff --git a/indexer/etl/l2_etl.go b/indexer/etl/l2_etl.go
index 72dfff8c7..ce2559e5d 100644
--- a/indexer/etl/l2_etl.go
+++ b/indexer/etl/l2_etl.go
@@ -2,12 +2,11 @@ package etl
 
 import (
 	"context"
-	"errors"
 	"time"
 
-	"github.com/ethereum-optimism/optimism/indexer/config"
 	"github.com/ethereum-optimism/optimism/indexer/database"
 	"github.com/ethereum-optimism/optimism/indexer/node"
+	"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
 	"github.com/ethereum-optimism/optimism/op-service/retry"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
@@ -20,25 +19,14 @@ type L2ETL struct {
 	db *database.DB
 }
 
-func NewL2ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, client node.EthClient, contracts config.L2Contracts) (*L2ETL, error) {
+func NewL2ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, client node.EthClient) (*L2ETL, error) {
 	log = log.New("etl", "l2")
 
-	zeroAddr := common.Address{}
+	// allow predeploys to be overridable
 	l2Contracts := []common.Address{}
-	if err := contracts.ForEach(func(name string, addr common.Address) error {
-		// Since we dont have backfill support yet, we want to make sure all expected
-		// contracts are specified to ensure consistent behavior. Once backfill support
-		// is ready, we can relax this requirement.
-		if addr == zeroAddr {
-			log.Error("address not configured", "name", name)
-			return errors.New("all L2Contracts must be configured")
-		}
-
+	for name, addr := range predeploys.Predeploys {
 		log.Info("configured contract", "name", name, "addr", addr)
-		l2Contracts = append(l2Contracts, addr)
-		return nil
-	}); err != nil {
-		return nil, err
+		l2Contracts = append(l2Contracts, *addr)
 	}
 
 	latestHeader, err := db.Blocks.L2LatestBlockHeader()
diff --git a/indexer/indexer.go b/indexer/indexer.go
index f3718ebbd..8f652fc5d 100644
--- a/indexer/indexer.go
+++ b/indexer/indexer.go
@@ -76,7 +76,7 @@ func NewIndexer(
 		HeaderBufferSize:  chainConfig.L2HeaderBufferSize,
 		ConfirmationDepth: big.NewInt(int64(chainConfig.L2ConfirmationDepth)),
 	}
-	l2Etl, err := etl.NewL2ETL(l2Cfg, log, db, etl.NewMetrics(metricsRegistry, "l2"), l2EthClient, chainConfig.L2Contracts)
+	l2Etl, err := etl.NewL2ETL(l2Cfg, log, db, etl.NewMetrics(metricsRegistry, "l2"), l2EthClient)
 	if err != nil {
 		return nil, err
 	}
diff --git a/indexer/migrations/20230523_create_schema.sql b/indexer/migrations/20230523_create_schema.sql
index 7cb965ac3..e205db9f2 100644
--- a/indexer/migrations/20230523_create_schema.sql
+++ b/indexer/migrations/20230523_create_schema.sql
@@ -57,7 +57,6 @@ CREATE TABLE IF NOT EXISTS l1_contract_events (
 CREATE INDEX IF NOT EXISTS l1_contract_events_timestamp ON l1_contract_events(timestamp);
 CREATE INDEX IF NOT EXISTS l1_contract_events_block_hash ON l1_contract_events(block_hash);
 CREATE INDEX IF NOT EXISTS l1_contract_events_event_signature ON l1_contract_events(event_signature);
-CREATE INDEX IF NOT EXISTS l1_contract_events_contract_address ON l1_contract_events(contract_address);
 
 CREATE TABLE IF NOT EXISTS l2_contract_events (
     -- Searchable fields
@@ -75,12 +74,52 @@ CREATE TABLE IF NOT EXISTS l2_contract_events (
 CREATE INDEX IF NOT EXISTS l2_contract_events_timestamp ON l2_contract_events(timestamp);
 CREATE INDEX IF NOT EXISTS l2_contract_events_block_hash ON l2_contract_events(block_hash);
 CREATE INDEX IF NOT EXISTS l2_contract_events_event_signature ON l2_contract_events(event_signature);
-CREATE INDEX IF NOT EXISTS l2_contract_events_contract_address ON l2_contract_events(contract_address);
+
+-- Tables that index finalization markers for L2 blocks.
+
+CREATE TABLE IF NOT EXISTS legacy_state_batches (
+    index      INTEGER PRIMARY KEY,
+    root       VARCHAR NOT NULL UNIQUE,
+    size       INTEGER NOT NULL,
+    prev_total INTEGER NOT NULL,
+
+    state_batch_appended_guid VARCHAR NOT NULL UNIQUE REFERENCES l1_contract_events(guid) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS output_proposals (
+    output_root     VARCHAR PRIMARY KEY,
+    l2_output_index UINT256 NOT NULL UNIQUE,
+    l2_block_number UINT256 NOT NULL UNIQUE,
+
+    output_proposed_guid VARCHAR NOT NULL UNIQUE REFERENCES l1_contract_events(guid) ON DELETE CASCADE
+);
+
 
 /**
  * BRIDGING DATA
  */
 
+-- Bridged L1/L2 Tokens
+CREATE TABLE IF NOT EXISTS l1_bridged_tokens (
+    address        VARCHAR PRIMARY KEY,
+    bridge_address VARCHAR NOT NULL,
+
+    name     VARCHAR NOT NULL,
+    symbol   VARCHAR NOT NULL,
+    decimals INTEGER NOT NULL CHECK (decimals >= 0 AND decimals <= 18)
+);
+CREATE TABLE IF NOT EXISTS l2_bridged_tokens (
+    address        VARCHAR PRIMARY KEY,
+    bridge_address VARCHAR NOT NULL,
+
+    -- L1-L2 relationship is 1 to many so this is not necessarily unique
+    l1_token_address VARCHAR REFERENCES l1_bridged_tokens(address) ON DELETE CASCADE,
+
+    name     VARCHAR NOT NULL,
+    symbol   VARCHAR NOT NULL,
+    decimals INTEGER NOT NULL CHECK (decimals >= 0 AND decimals <= 18)
+);
+
 -- OptimismPortal/L2ToL1MessagePasser
 CREATE TABLE IF NOT EXISTS l1_transaction_deposits (
     source_hash             VARCHAR PRIMARY KEY,
@@ -163,26 +202,6 @@ CREATE INDEX IF NOT EXISTS l2_bridge_messages_transaction_withdrawal_hash ON l2_
 CREATE INDEX IF NOT EXISTS l2_bridge_messages_from_address ON l2_bridge_messages(from_address);
 
 -- StandardBridge
-CREATE TABLE IF NOT EXISTS l1_bridged_tokens (
-    address        VARCHAR PRIMARY KEY,
-    bridge_address VARCHAR NOT NULL,
-
-    name     VARCHAR NOT NULL,
-    symbol   VARCHAR NOT NULL,
-    decimals INTEGER NOT NULL CHECK (decimals >= 0 AND decimals <= 18)
-);
-CREATE TABLE IF NOT EXISTS l2_bridged_tokens (
-    address        VARCHAR PRIMARY KEY,
-    bridge_address VARCHAR NOT NULL,
-
-    -- L1-L2 relationship is 1 to many so this is not necessarily unique
-    l1_token_address VARCHAR REFERENCES l1_bridged_tokens(address) ON DELETE CASCADE,
-
-    name     VARCHAR NOT NULL,
-    symbol   VARCHAR NOT NULL,
-    decimals INTEGER NOT NULL CHECK (decimals >= 0 AND decimals <= 18)
-);
-
 CREATE TABLE IF NOT EXISTS l1_bridge_deposits (
     transaction_source_hash   VARCHAR PRIMARY KEY REFERENCES l1_transaction_deposits(source_hash) ON DELETE CASCADE,
     cross_domain_message_hash VARCHAR NOT NULL UNIQUE REFERENCES l1_bridge_messages(message_hash) ON DELETE CASCADE,
diff --git a/indexer/processors/bridge.go b/indexer/processors/bridge.go
index 07d11bdda..992928498 100644
--- a/indexer/processors/bridge.go
+++ b/indexer/processors/bridge.go
@@ -155,14 +155,14 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
 				for _, group := range l1BlockGroups {
 					log := l1BridgeLog.New("from_l1_block_number", group.Start, "to_l1_block_number", group.End)
 					log.Info("scanning for initiated bridge events")
-					if err := bridge.LegacyL1ProcessInitiatedBridgeEvents(log, tx, b.chainConfig.L1Contracts, group.Start, group.End); err != nil {
+					if err := bridge.LegacyL1ProcessInitiatedBridgeEvents(log, tx, b.chainConfig, group.Start, group.End); err != nil {
 						return err
 					}
 				}
 				for _, group := range l2BlockGroups {
 					log := l2BridgeLog.New("from_l2_block_number", group.Start, "to_l2_block_number", group.End)
 					log.Info("scanning for initiated bridge events")
-					if err := bridge.LegacyL2ProcessInitiatedBridgeEvents(log, tx, b.chainConfig.L2Contracts, group.Start, group.End); err != nil {
+					if err := bridge.LegacyL2ProcessInitiatedBridgeEvents(log, tx, group.Start, group.End); err != nil {
 						return err
 					}
 				}
@@ -171,14 +171,14 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
 				for _, group := range l1BlockGroups {
 					log := l1BridgeLog.New("from_l1_block_number", group.Start, "to_l1_block_number", group.End)
 					log.Info("scanning for finalized bridge events")
-					if err := bridge.LegacyL1ProcessFinalizedBridgeEvents(log, tx, b.l1Etl.EthClient, b.chainConfig.L1Contracts, group.Start, group.End); err != nil {
+					if err := bridge.LegacyL1ProcessFinalizedBridgeEvents(log, tx, b.l1Etl.EthClient, b.chainConfig, group.Start, group.End); err != nil {
 						return err
 					}
 				}
 				for _, group := range l2BlockGroups {
 					log := l2BridgeLog.New("from_l2_block_number", group.Start, "to_l2_block_number", group.End)
 					log.Info("scanning for finalized bridge events")
-					if err := bridge.LegacyL2ProcessFinalizedBridgeEvents(log, tx, b.chainConfig.L2Contracts, group.Start, group.End); err != nil {
+					if err := bridge.LegacyL2ProcessFinalizedBridgeEvents(log, tx, group.Start, group.End); err != nil {
 						return err
 					}
 				}
@@ -199,14 +199,14 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
 			for _, group := range l1BlockGroups {
 				log := l1BridgeLog.New("from_block_number", group.Start, "to_block_number", group.End)
 				log.Info("scanning for initiated bridge events")
-				if err := bridge.L1ProcessInitiatedBridgeEvents(log, tx, b.chainConfig.L1Contracts, group.Start, group.End); err != nil {
+				if err := bridge.L1ProcessInitiatedBridgeEvents(log, tx, b.chainConfig, group.Start, group.End); err != nil {
 					return err
 				}
 			}
 			for _, group := range l2BlockGroups {
 				log := l2BridgeLog.New("from_block_number", group.Start, "to_block_number", group.End)
 				log.Info("scanning for initiated bridge events")
-				if err := bridge.L2ProcessInitiatedBridgeEvents(log, tx, b.chainConfig.L2Contracts, group.Start, group.End); err != nil {
+				if err := bridge.L2ProcessInitiatedBridgeEvents(log, tx, group.Start, group.End); err != nil {
 					return err
 				}
 			}
@@ -215,14 +215,14 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
 			for _, group := range l1BlockGroups {
 				log := l1BridgeLog.New("from_block_number", group.Start, "to_block_number", group.End)
 				log.Info("scanning for finalized bridge events")
-				if err := bridge.L1ProcessFinalizedBridgeEvents(log, tx, b.chainConfig.L1Contracts, group.Start, group.End); err != nil {
+				if err := bridge.L1ProcessFinalizedBridgeEvents(log, tx, b.chainConfig, group.Start, group.End); err != nil {
 					return err
 				}
 			}
 			for _, group := range l2BlockGroups {
 				log := l2BridgeLog.New("from_block_number", group.Start, "to_block_number", group.End)
 				log.Info("scanning for finalized bridge events")
-				if err := bridge.L2ProcessFinalizedBridgeEvents(log, tx, b.chainConfig.L2Contracts, group.Start, group.End); err != nil {
+				if err := bridge.L2ProcessFinalizedBridgeEvents(log, tx, group.Start, group.End); err != nil {
 					return err
 				}
 			}
diff --git a/indexer/processors/bridge/l1_bridge_processor.go b/indexer/processors/bridge/l1_bridge_processor.go
index 51baf9d6f..a35611fad 100644
--- a/indexer/processors/bridge/l1_bridge_processor.go
+++ b/indexer/processors/bridge/l1_bridge_processor.go
@@ -17,9 +17,9 @@ import (
 //  1. OptimismPortal
 //  2. L1CrossDomainMessenger
 //  3. L1StandardBridge
-func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
+func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, chainConfig config.ChainConfig, fromHeight *big.Int, toHeight *big.Int) error {
 	// (1) OptimismPortal
-	optimismPortalTxDeposits, err := contracts.OptimismPortalTransactionDepositEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
+	optimismPortalTxDeposits, err := contracts.OptimismPortalTransactionDepositEvents(chainConfig.L1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -47,7 +47,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
 	}
 
 	// (2) L1CrossDomainMessenger
-	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
+	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l1", chainConfig.L1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -77,7 +77,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
 	}
 
 	// (3) L1StandardBridge
-	initiatedBridges, err := contracts.StandardBridgeInitiatedEvents("l1", l1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
+	initiatedBridges, err := contracts.StandardBridgeInitiatedEvents("l1", chainConfig.L1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -121,9 +121,9 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
 //  1. OptimismPortal (Bedrock prove & finalize steps)
 //  2. L1CrossDomainMessenger (relayMessage marker)
 //  3. L1StandardBridge (no-op, since this is simply a wrapper over the L1CrossDomainMessenger)
-func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
+func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, chainConfig config.ChainConfig, fromHeight *big.Int, toHeight *big.Int) error {
 	// (1) OptimismPortal (proven withdrawals)
-	provenWithdrawals, err := contracts.OptimismPortalWithdrawalProvenEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
+	provenWithdrawals, err := contracts.OptimismPortalWithdrawalProvenEvents(chainConfig.L1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -148,7 +148,7 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
 	}
 
 	// (2) OptimismPortal (finalized withdrawals)
-	finalizedWithdrawals, err := contracts.OptimismPortalWithdrawalFinalizedEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
+	finalizedWithdrawals, err := contracts.OptimismPortalWithdrawalFinalizedEvents(chainConfig.L1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -173,7 +173,7 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
 	}
 
 	// (3) L1CrossDomainMessenger
-	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
+	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", chainConfig.L1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -200,7 +200,7 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
 	}
 
 	// (4) L1StandardBridge
-	finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l1", l1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
+	finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l1", chainConfig.L1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
diff --git a/indexer/processors/bridge/l2_bridge_processor.go b/indexer/processors/bridge/l2_bridge_processor.go
index 0229bf291..790faede6 100644
--- a/indexer/processors/bridge/l2_bridge_processor.go
+++ b/indexer/processors/bridge/l2_bridge_processor.go
@@ -5,9 +5,9 @@ import (
 	"fmt"
 	"math/big"
 
-	"github.com/ethereum-optimism/optimism/indexer/config"
 	"github.com/ethereum-optimism/optimism/indexer/database"
 	"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
+	"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
 
 	"github.com/ethereum/go-ethereum/log"
 )
@@ -17,9 +17,9 @@ import (
 //  1. OptimismPortal
 //  2. L2CrossDomainMessenger
 //  3. L2StandardBridge
-func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
+func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, fromHeight *big.Int, toHeight *big.Int) error {
 	// (1) L2ToL1MessagePasser
-	l2ToL1MPMessagesPassed, err := contracts.L2ToL1MessagePasserMessagePassedEvents(l2Contracts.L2ToL1MessagePasser, db, fromHeight, toHeight)
+	l2ToL1MPMessagesPassed, err := contracts.L2ToL1MessagePasserMessagePassedEvents(predeploys.L2ToL1MessagePasserAddr, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -47,7 +47,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
 	}
 
 	// (2) L2CrossDomainMessenger
-	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
+	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", predeploys.L2CrossDomainMessengerAddr, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -78,7 +78,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
 	}
 
 	// (3) L2StandardBridge
-	initiatedBridges, err := contracts.StandardBridgeInitiatedEvents("l2", l2Contracts.L2StandardBridge, db, fromHeight, toHeight)
+	initiatedBridges, err := contracts.StandardBridgeInitiatedEvents("l2", predeploys.L2StandardBridgeAddr, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -122,9 +122,9 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
 //  2. L2StandardBridge (no-op, since this is simply a wrapper over the L2CrossDomainMEssenger)
 //
 // NOTE: Unlike L1, there's no L2ToL1MessagePasser stage since transaction deposits are apart of the block derivation process.
-func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
+func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, fromHeight *big.Int, toHeight *big.Int) error {
 	// (1) L2CrossDomainMessenger
-	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
+	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", predeploys.L2CrossDomainMessengerAddr, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -151,7 +151,7 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
 	}
 
 	// (2) L2StandardBridge
-	finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l2", l2Contracts.L2StandardBridge, db, fromHeight, toHeight)
+	finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l2", predeploys.L2StandardBridgeAddr, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
diff --git a/indexer/processors/bridge/legacy_bridge_processor.go b/indexer/processors/bridge/legacy_bridge_processor.go
index 6c60fe34f..f78513959 100644
--- a/indexer/processors/bridge/legacy_bridge_processor.go
+++ b/indexer/processors/bridge/legacy_bridge_processor.go
@@ -11,6 +11,7 @@ import (
 	"github.com/ethereum-optimism/optimism/indexer/database"
 	"github.com/ethereum-optimism/optimism/indexer/node"
 	"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
+	"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
 )
 
 // Legacy Bridge Initiation
@@ -20,9 +21,9 @@ import (
 //  1. CanonicalTransactionChain
 //  2. L1CrossDomainMessenger
 //  3. L1StandardBridge
-func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
+func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, chainConfig config.ChainConfig, fromHeight *big.Int, toHeight *big.Int) error {
 	// (1) CanonicalTransactionChain
-	ctcTxDepositEvents, err := contracts.LegacyCTCDepositEvents(l1Contracts.LegacyCanonicalTransactionChain, db, fromHeight, toHeight)
+	ctcTxDepositEvents, err := contracts.LegacyCTCDepositEvents(chainConfig.L1Contracts.LegacyCanonicalTransactionChain, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -53,7 +54,7 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
 	}
 
 	// (2) L1CrossDomainMessenger
-	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
+	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l1", chainConfig.L1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -83,7 +84,7 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
 	}
 
 	// (3) L1StandardBridge
-	initiatedBridges, err := contracts.L1StandardBridgeLegacyDepositInitiatedEvents(l1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
+	initiatedBridges, err := contracts.L1StandardBridgeLegacyDepositInitiatedEvents(chainConfig.L1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -130,9 +131,9 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
 //  1. L2CrossDomainMessenger - The LegacyMessagePasser contract cannot be used as entrypoint to bridge transactions from L2. The protocol
 //     only allows the L2CrossDomainMessenger as the sole sender when relaying a bridged message.
 //  2. L2StandardBridge
-func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
+func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, fromHeight *big.Int, toHeight *big.Int) error {
 	// (1) L2CrossDomainMessenger
-	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
+	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", predeploys.L2CrossDomainMessengerAddr, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -150,7 +151,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
 
 		// To ensure consistency in the schema, we duplicate this as the "root" transaction withdrawal. The storage key in the message
 		// passer contract is sha3(calldata + sender). The sender always being the L2CrossDomainMessenger pre-bedrock.
-		withdrawalHash := crypto.Keccak256Hash(append(sentMessage.MessageCalldata, l2Contracts.L2CrossDomainMessenger[:]...))
+		withdrawalHash := crypto.Keccak256Hash(append(sentMessage.MessageCalldata, predeploys.L2CrossDomainMessengerAddr[:]...))
 		l2TransactionWithdrawals[i] = database.L2TransactionWithdrawal{
 			WithdrawalHash:       withdrawalHash,
 			InitiatedL2EventGUID: sentMessage.Event.GUID,
@@ -181,7 +182,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
 	}
 
 	// (2) L2StandardBridge
-	initiatedBridges, err := contracts.L2StandardBridgeLegacyWithdrawalInitiatedEvents(l2Contracts.L2StandardBridge, db, fromHeight, toHeight)
+	initiatedBridges, err := contracts.L2StandardBridgeLegacyWithdrawalInitiatedEvents(predeploys.L2StandardBridgeAddr, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -224,10 +225,10 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
 // according to the pre-bedrock protocol. This follows:
 //  1. L1CrossDomainMessenger
 //  2. L1StandardBridge
-func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Client node.EthClient, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
+func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Client node.EthClient, chainConfig config.ChainConfig, fromHeight *big.Int, toHeight *big.Int) error {
 	// (1) L1CrossDomainMessenger -> This is the root-most contract from which bridge events are finalized since withdrawals must be initiated from the
 	// L2CrossDomainMessenger. Since there's no two-step withdrawal process, we mark the transaction as proven/finalized in the same step
-	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
+	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", chainConfig.L1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
@@ -304,9 +305,9 @@ func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Cli
 // according to the pre-bedrock protocol. This follows:
 //  1. L2CrossDomainMessenger
 //  2. L2StandardBridge
-func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
+func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, fromHeight *big.Int, toHeight *big.Int) error {
 	// (1) L2CrossDomainMessenger
-	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
+	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", predeploys.L2CrossDomainMessengerAddr, db, fromHeight, toHeight)
 	if err != nil {
 		return err
 	}
diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go
index bd4fed46b..1ea9d5d0d 100644
--- a/op-chain-ops/genesis/config.go
+++ b/op-chain-ops/genesis/config.go
@@ -15,6 +15,7 @@ import (
 	gstate "github.com/ethereum/go-ethereum/core/state"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/log"
+	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/rpc"
 
 	"github.com/ethereum-optimism/optimism/op-bindings/hardhat"
@@ -199,10 +200,10 @@ type DeployConfig struct {
 	FundDevAccounts bool `json:"fundDevAccounts"`
 	// RequiredProtocolVersion indicates the protocol version that
 	// nodes are required to adopt, to stay in sync with the network.
-	RequiredProtocolVersion common.Hash `json:"requiredProtocolVersion"`
+	RequiredProtocolVersion params.ProtocolVersion `json:"requiredProtocolVersion"`
 	// RequiredProtocolVersion indicates the protocol version that
 	// nodes are recommended to adopt, to stay in sync with the network.
-	RecommendedProtocolVersion common.Hash `json:"recommendedProtocolVersion"`
+	RecommendedProtocolVersion params.ProtocolVersion `json:"recommendedProtocolVersion"`
 }
 
 // Copy will deeply copy the DeployConfig. This does a JSON roundtrip to copy
diff --git a/op-challenger/README.md b/op-challenger/README.md
index d3bdc9039..a7f30875c 100644
--- a/op-challenger/README.md
+++ b/op-challenger/README.md
@@ -103,10 +103,6 @@ Starts a new fault dispute game that disputes the latest output proposal in the
 * `RPC_URL` - the RPC endpoint of the L1 endpoint to use (e.g. `http://localhost:8545`).
 * `GAME_FACTORY_ADDRESS` - the address of the dispute game factory contract on L1.
 * `ROOT_CLAIM` a hex encoded 32 byte hash to use as the root claim for the created game.
-    * The root claim must have the high-order byte set to the
-      invalid [VM status](../specs/cannon-fault-proof-vm.md#state-hash) (`0x01`) to indicate that the trace concludes
-      that the disputed output root is invalid.
-      e.g. `0x0146381068b59d2098495baa72ed2f773c1e09458610a7a208984859dff73add`
 * `SIGNER_ARGS` the remaining args are past as arguments to `cast` when sending transactions.
   These arguments must specify a way for `cast` to sign the transactions.
   See `cast send --help` for supported options.
diff --git a/op-challenger/config/config.go b/op-challenger/config/config.go
index fcc6de0ec..0bc10c3ea 100644
--- a/op-challenger/config/config.go
+++ b/op-challenger/config/config.go
@@ -78,7 +78,6 @@ func ValidTraceType(value TraceType) bool {
 }
 
 const (
-	DefaultPollInterval       = time.Second * 12
 	DefaultCannonSnapshotFreq = uint(1_000_000_000)
 	DefaultCannonInfoFreq     = uint(10_000_000)
 	// DefaultGameWindow is the default maximum time duration in the past
@@ -99,7 +98,6 @@ type Config struct {
 	AgreeWithProposedOutput bool             // Temporary config if we agree or disagree with the posted output
 	Datadir                 string           // Data Directory
 	MaxConcurrency          uint             // Maximum number of threads to use when progressing games
-	PollInterval            time.Duration    // Polling interval for latest-block subscription when using an HTTP RPC provider
 
 	TraceType TraceType // Type of trace
 
@@ -133,7 +131,6 @@ func NewConfig(
 		L1EthRpc:           l1EthRpc,
 		GameFactoryAddress: gameFactoryAddress,
 		MaxConcurrency:     uint(runtime.NumCPU()),
-		PollInterval:       DefaultPollInterval,
 
 		AgreeWithProposedOutput: agreeWithProposedOutput,
 
diff --git a/op-challenger/flags/flags.go b/op-challenger/flags/flags.go
index 090a57ac8..ca098410a 100644
--- a/op-challenger/flags/flags.go
+++ b/op-challenger/flags/flags.go
@@ -70,12 +70,6 @@ var (
 		EnvVars: prefixEnvVars("MAX_CONCURRENCY"),
 		Value:   uint(runtime.NumCPU()),
 	}
-	HTTPPollInterval = &cli.DurationFlag{
-		Name:    "http-poll-interval",
-		Usage:   "Polling interval for latest-block subscription when using an HTTP RPC provider.",
-		EnvVars: prefixEnvVars("HTTP_POLL_INTERVAL"),
-		Value:   config.DefaultPollInterval,
-	}
 	AlphabetFlag = &cli.StringFlag{
 		Name:    "alphabet",
 		Usage:   "Correct Alphabet Trace (alphabet trace type only)",
@@ -148,7 +142,6 @@ var requiredFlags = []cli.Flag{
 // optionalFlags is a list of unchecked cli flags
 var optionalFlags = []cli.Flag{
 	MaxConcurrencyFlag,
-	HTTPPollInterval,
 	AlphabetFlag,
 	GameAllowlistFlag,
 	CannonNetworkFlag,
@@ -254,7 +247,6 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
 		GameAllowlist:           allowedGames,
 		GameWindow:              ctx.Duration(GameWindowFlag.Name),
 		MaxConcurrency:          maxConcurrency,
-		PollInterval:            ctx.Duration(HTTPPollInterval.Name),
 		AlphabetTrace:           ctx.String(AlphabetFlag.Name),
 		CannonNetwork:           ctx.String(CannonNetworkFlag.Name),
 		CannonRollupConfigPath:  ctx.String(CannonRollupConfigFlag.Name),
diff --git a/op-challenger/game/fault/agent.go b/op-challenger/game/fault/agent.go
index 4e48c40ee..199654088 100644
--- a/op-challenger/game/fault/agent.go
+++ b/op-challenger/game/fault/agent.go
@@ -9,7 +9,6 @@ import (
 	"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
 	gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
 	"github.com/ethereum-optimism/optimism/op-challenger/metrics"
-	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/log"
 )
 
@@ -67,12 +66,7 @@ func (a *Agent) Act(ctx context.Context) error {
 
 	// Perform the actions
 	for _, action := range actions {
-		log := a.log.New("action", action.Type, "is_attack", action.IsAttack, "parent", action.ParentIdx)
-		if action.Type == types.ActionTypeStep {
-			log = log.New("prestate", common.Bytes2Hex(action.PreState), "proof", common.Bytes2Hex(action.ProofData))
-		} else {
-			log = log.New("value", action.Value)
-		}
+		log := a.log.New("action", action.Type, "is_attack", action.IsAttack, "parent", action.ParentIdx, "value", action.Value)
 
 		if action.OracleData != nil {
 			a.log.Info("Updating oracle data", "oracleKey", action.OracleData.OracleKey, "oracleData", action.OracleData.OracleData)
diff --git a/op-challenger/game/fault/trace/cannon/executor.go b/op-challenger/game/fault/trace/cannon/executor.go
index eeb24dd73..6f15aed6b 100644
--- a/op-challenger/game/fault/trace/cannon/executor.go
+++ b/op-challenger/game/fault/trace/cannon/executor.go
@@ -124,7 +124,10 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro
 	e.logger.Info("Generating trace", "proof", i, "cmd", e.cannon, "args", strings.Join(args, ", "))
 	execStart := time.Now()
 	err = e.cmdExecutor(ctx, e.logger.New("proof", i), e.cannon, args...)
-	e.metrics.RecordCannonExecutionTime(time.Since(execStart).Seconds())
+	if err != nil {
+		execDuration := time.Since(execStart).Seconds()
+		e.metrics.RecordCannonExecutionTime(execDuration)
+	}
 	return err
 }
 
diff --git a/op-challenger/game/fault/trace/cannon/provider.go b/op-challenger/game/fault/trace/cannon/provider.go
index c4a8648d2..f4e14814d 100644
--- a/op-challenger/game/fault/trace/cannon/provider.go
+++ b/op-challenger/game/fault/trace/cannon/provider.go
@@ -22,8 +22,7 @@ import (
 )
 
 const (
-	proofsDir      = "proofs"
-	diskStateCache = "state.json.gz"
+	proofsDir = "proofs"
 )
 
 type proofData struct {
@@ -53,6 +52,8 @@ type CannonTraceProvider struct {
 	// lastStep stores the last step in the actual trace if known. 0 indicates unknown.
 	// Cached as an optimisation to avoid repeatedly attempting to execute beyond the end of the trace.
 	lastStep uint64
+	// lastProof stores the proof data to use for all steps extended beyond lastStep
+	lastProof *proofData
 }
 
 func NewTraceProvider(ctx context.Context, logger log.Logger, m CannonMetricer, cfg *config.Config, l1Client bind.ContractCaller, dir string, gameAddr common.Address) (*CannonTraceProvider, error) {
@@ -137,18 +138,9 @@ func (p *CannonTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (c
 // loadProof will attempt to load or generate the proof data at the specified index
 // If the requested index is beyond the end of the actual trace it is extended with no-op instructions.
 func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*proofData, error) {
-	// Attempt to read the last step from disk cache
-	if p.lastStep == 0 {
-		step, err := readLastStep(p.dir)
-		if err != nil {
-			p.logger.Warn("Failed to read last step from disk cache", "err", err)
-		} else {
-			p.lastStep = step
-		}
-	}
-	// If the last step is tracked, set i to the last step to generate or load the final proof
-	if p.lastStep != 0 && i > p.lastStep {
-		i = p.lastStep
+	if p.lastProof != nil && i > p.lastStep {
+		// If the requested index is after the last step in the actual trace, extend the final no-op step
+		return p.lastProof, nil
 	}
 	path := filepath.Join(p.dir, proofsDir, fmt.Sprintf("%d.json.gz", i))
 	file, err := ioutil.OpenDecompressed(path)
@@ -184,9 +176,7 @@ func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*proofDa
 					OracleValue:  nil,
 					OracleOffset: 0,
 				}
-				if err := writeLastStep(p.dir, proof, p.lastStep); err != nil {
-					p.logger.Warn("Failed to write last step to disk cache", "step", p.lastStep)
-				}
+				p.lastProof = proof
 				return proof, nil
 			} else {
 				return nil, fmt.Errorf("expected proof not generated but final state was not exited, requested step %v, final state at step %v", i, state.Step)
@@ -204,35 +194,3 @@ func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*proofDa
 	}
 	return &proof, nil
 }
-
-type diskStateCacheObj struct {
-	Step uint64 `json:"step"`
-}
-
-// readLastStep reads the tracked last step from disk.
-func readLastStep(dir string) (uint64, error) {
-	state := diskStateCacheObj{}
-	file, err := ioutil.OpenDecompressed(filepath.Join(dir, diskStateCache))
-	if err != nil {
-		return 0, err
-	}
-	defer file.Close()
-	err = json.NewDecoder(file).Decode(&state)
-	if err != nil {
-		return 0, err
-	}
-	return state.Step, nil
-}
-
-// writeLastStep writes the last step and proof to disk as a persistent cache.
-func writeLastStep(dir string, proof *proofData, step uint64) error {
-	state := diskStateCacheObj{Step: step}
-	lastStepFile := filepath.Join(dir, diskStateCache)
-	if err := ioutil.WriteCompressedJson(lastStepFile, state); err != nil {
-		return fmt.Errorf("failed to write last step to %v: %w", lastStepFile, err)
-	}
-	if err := ioutil.WriteCompressedJson(filepath.Join(dir, proofsDir, fmt.Sprintf("%d.json.gz", step)), proof); err != nil {
-		return fmt.Errorf("failed to write proof: %w", err)
-	}
-	return nil
-}
diff --git a/op-challenger/game/monitor.go b/op-challenger/game/monitor.go
index 74b8f5622..6f205fab3 100644
--- a/op-challenger/game/monitor.go
+++ b/op-challenger/game/monitor.go
@@ -9,12 +9,7 @@ import (
 
 	"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
 	"github.com/ethereum-optimism/optimism/op-service/clock"
-	"github.com/ethereum-optimism/optimism/op-service/eth"
-
-	"github.com/ethereum/go-ethereum"
 	"github.com/ethereum/go-ethereum/common"
-	ethTypes "github.com/ethereum/go-ethereum/core/types"
-	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/log"
 )
 
@@ -37,20 +32,6 @@ type gameMonitor struct {
 	gameWindow       time.Duration
 	fetchBlockNumber blockNumberFetcher
 	allowedGames     []common.Address
-	l1HeadsSub       ethereum.Subscription
-	l1Source         *headSource
-}
-
-type MinimalSubscriber interface {
-	EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (ethereum.Subscription, error)
-}
-
-type headSource struct {
-	inner MinimalSubscriber
-}
-
-func (s *headSource) SubscribeNewHead(ctx context.Context, ch chan<- *ethTypes.Header) (ethereum.Subscription, error) {
-	return s.inner.EthSubscribe(ctx, ch, "newHeads")
 }
 
 func newGameMonitor(
@@ -61,7 +42,6 @@ func newGameMonitor(
 	gameWindow time.Duration,
 	fetchBlockNumber blockNumberFetcher,
 	allowedGames []common.Address,
-	l1Source MinimalSubscriber,
 ) *gameMonitor {
 	return &gameMonitor{
 		logger:           logger,
@@ -71,7 +51,6 @@ func newGameMonitor(
 		gameWindow:       gameWindow,
 		fetchBlockNumber: fetchBlockNumber,
 		allowedGames:     allowedGames,
-		l1Source:         &headSource{inner: l1Source},
 	}
 }
 
@@ -120,32 +99,29 @@ func (m *gameMonitor) progressGames(ctx context.Context, blockNum uint64) error
 	return nil
 }
 
-func (m *gameMonitor) onNewL1Head(ctx context.Context, sig eth.L1BlockRef) {
-	if err := m.progressGames(ctx, sig.Number); err != nil {
-		m.logger.Error("Failed to progress games", "err", err)
-	}
-}
-
-func (m *gameMonitor) resubscribeFunction(ctx context.Context) event.ResubscribeErrFunc {
-	return func(innerCtx context.Context, err error) (event.Subscription, error) {
-		if err != nil {
-			m.logger.Warn("resubscribing after failed L1 subscription", "err", err)
-		}
-		return eth.WatchHeadChanges(ctx, m.l1Source, m.onNewL1Head)
-	}
-}
-
 func (m *gameMonitor) MonitorGames(ctx context.Context) error {
-	m.l1HeadsSub = event.ResubscribeErr(time.Second*10, m.resubscribeFunction(ctx))
+	m.logger.Info("Monitoring fault dispute games")
+
+	blockNum := uint64(0)
 	for {
 		select {
 		case <-ctx.Done():
-			return nil
-		case err, ok := <-m.l1HeadsSub.Err():
-			if !ok {
+			return ctx.Err()
+		default:
+			nextBlockNum, err := m.fetchBlockNumber(ctx)
+			if err != nil {
+				m.logger.Error("Failed to load current block number", "err", err)
+				continue
+			}
+			if nextBlockNum > blockNum {
+				blockNum = nextBlockNum
+				if err := m.progressGames(ctx, nextBlockNum); err != nil {
+					m.logger.Error("Failed to progress games", "err", err)
+				}
+			}
+			if err := m.clock.SleepCtx(ctx, time.Second); err != nil {
 				return err
 			}
-			m.logger.Error("L1 subscription error", "err", err)
 		}
 	}
 }
diff --git a/op-challenger/game/service.go b/op-challenger/game/service.go
index ac526e05f..8ec157372 100644
--- a/op-challenger/game/service.go
+++ b/op-challenger/game/service.go
@@ -10,7 +10,6 @@ import (
 	"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
 	"github.com/ethereum-optimism/optimism/op-challenger/metrics"
 	"github.com/ethereum-optimism/optimism/op-challenger/version"
-	opClient "github.com/ethereum-optimism/optimism/op-node/client"
 	"github.com/ethereum-optimism/optimism/op-service/client"
 	"github.com/ethereum-optimism/optimism/op-service/clock"
 	oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
@@ -35,7 +34,7 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*Se
 		return nil, fmt.Errorf("failed to create the transaction manager: %w", err)
 	}
 
-	l1Client, err := client.DialEthClientWithTimeout(client.DefaultDialTimeout, logger, cfg.L1EthRpc)
+	client, err := client.DialEthClientWithTimeout(client.DefaultDialTimeout, logger, cfg.L1EthRpc)
 	if err != nil {
 		return nil, fmt.Errorf("failed to dial L1: %w", err)
 	}
@@ -58,10 +57,10 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*Se
 				logger.Error("error starting metrics server", "err", err)
 			}
 		}()
-		m.StartBalanceMetrics(ctx, logger, l1Client, txMgr.From())
+		m.StartBalanceMetrics(ctx, logger, client, txMgr.From())
 	}
 
-	factory, err := bindings.NewDisputeGameFactory(cfg.GameFactoryAddress, l1Client)
+	factory, err := bindings.NewDisputeGameFactory(cfg.GameFactoryAddress, client)
 	if err != nil {
 		return nil, fmt.Errorf("failed to bind the fault dispute game factory contract: %w", err)
 	}
@@ -74,14 +73,10 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*Se
 		disk,
 		cfg.MaxConcurrency,
 		func(addr common.Address, dir string) (scheduler.GamePlayer, error) {
-			return fault.NewGamePlayer(ctx, logger, m, cfg, dir, addr, txMgr, l1Client)
+			return fault.NewGamePlayer(ctx, logger, m, cfg, dir, addr, txMgr, client)
 		})
 
-	pollClient, err := opClient.NewRPCWithClient(ctx, logger, cfg.L1EthRpc, opClient.NewBaseRPCClient(l1Client.Client()), cfg.PollInterval)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create RPC client: %w", err)
-	}
-	monitor := newGameMonitor(logger, cl, loader, sched, cfg.GameWindow, l1Client.BlockNumber, cfg.GameAllowlist, pollClient)
+	monitor := newGameMonitor(logger, cl, loader, sched, cfg.GameWindow, client.BlockNumber, cfg.GameAllowlist)
 
 	m.RecordInfo(version.SimpleWithMeta)
 	m.RecordUp()
diff --git a/op-challenger/metrics/noop.go b/op-challenger/metrics/noop.go
index ef7452eef..d02bd594f 100644
--- a/op-challenger/metrics/noop.go
+++ b/op-challenger/metrics/noop.go
@@ -4,21 +4,21 @@ import (
 	txmetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
 )
 
-type NoopMetricsImpl struct {
+type noopMetrics struct {
 	txmetrics.NoopTxMetrics
 }
 
-var NoopMetrics Metricer = new(NoopMetricsImpl)
+var NoopMetrics Metricer = new(noopMetrics)
 
-func (*NoopMetricsImpl) RecordInfo(version string) {}
-func (*NoopMetricsImpl) RecordUp()                 {}
+func (*noopMetrics) RecordInfo(version string) {}
+func (*noopMetrics) RecordUp()                 {}
 
-func (*NoopMetricsImpl) RecordGameMove() {}
-func (*NoopMetricsImpl) RecordGameStep() {}
+func (*noopMetrics) RecordGameMove() {}
+func (*noopMetrics) RecordGameStep() {}
 
-func (*NoopMetricsImpl) RecordCannonExecutionTime(t float64) {}
+func (*noopMetrics) RecordCannonExecutionTime(t float64) {}
 
-func (*NoopMetricsImpl) RecordGamesStatus(inProgress, defenderWon, challengerWon int) {}
+func (*noopMetrics) RecordGamesStatus(inProgress, defenderWon, challengerWon int) {}
 
-func (*NoopMetricsImpl) RecordGameUpdateScheduled() {}
-func (*NoopMetricsImpl) RecordGameUpdateCompleted() {}
+func (*noopMetrics) RecordGameUpdateScheduled() {}
+func (*noopMetrics) RecordGameUpdateCompleted() {}
diff --git a/op-challenger/scripts/list_games.sh b/op-challenger/scripts/list_games.sh
index 6a00f8ac9..b46e540fa 100755
--- a/op-challenger/scripts/list_games.sh
+++ b/op-challenger/scripts/list_games.sh
@@ -22,7 +22,6 @@ do
   IFS=$SAVEIFS   # Restore original IFS
 
   GAME_ADDR="${GAME[2]}"
-  CLAIMS=$(cast call --rpc-url "${RPC}" "${GAME_ADDR}" "claimDataLen() returns(uint256)")
   STATUS=$(cast call --rpc-url "${RPC}" "${GAME_ADDR}" "status() return(uint8)" | cast to-dec)
   if [[ "${STATUS}" == "0" ]]
   then
@@ -34,5 +33,5 @@ do
   then
     STATUS="Defender Wins"
   fi
-  echo "${i}  Game: ${GAME_ADDR} Type: ${GAME[0]} Created: ${GAME[1]} Claims: ${CLAIMS} Status: ${STATUS}"
+  echo "${i}  Game: ${GAME_ADDR} Type: ${GAME[0]} Created: ${GAME[1]} Status: ${STATUS}"
 done
diff --git a/op-node/client/rpc.go b/op-node/client/rpc.go
index cc48b8b1b..f1d6dab9b 100644
--- a/op-node/client/rpc.go
+++ b/op-node/client/rpc.go
@@ -80,11 +80,9 @@ func NewRPC(ctx context.Context, lgr log.Logger, addr string, opts ...RPCOption)
 			return nil, fmt.Errorf("rpc option %d failed to apply to RPC config: %w", i, err)
 		}
 	}
-
 	if cfg.backoffAttempts < 1 { // default to at least 1 attempt, or it always fails to dial.
 		cfg.backoffAttempts = 1
 	}
-
 	underlying, err := dialRPCClientWithBackoff(ctx, lgr, addr, cfg.backoffAttempts, cfg.gethRPCOptions...)
 	if err != nil {
 		return nil, err
@@ -96,15 +94,11 @@ func NewRPC(ctx context.Context, lgr log.Logger, addr string, opts ...RPCOption)
 		wrapped = NewRateLimitingClient(wrapped, rate.Limit(cfg.limit), cfg.burst)
 	}
 
-	return NewRPCWithClient(ctx, lgr, addr, wrapped, cfg.httpPollInterval)
-}
-
-// NewRPCWithClient builds a new polling client with the given underlying RPC client.
-func NewRPCWithClient(ctx context.Context, lgr log.Logger, addr string, underlying RPC, pollInterval time.Duration) (RPC, error) {
 	if httpRegex.MatchString(addr) {
-		underlying = NewPollingClient(ctx, lgr, underlying, WithPollRate(pollInterval))
+		wrapped = NewPollingClient(ctx, lgr, wrapped, WithPollRate(cfg.httpPollInterval))
 	}
-	return underlying, nil
+
+	return wrapped, nil
 }
 
 // Dials a JSON-RPC endpoint repeatedly, with a backoff, until a client connection is established. Auth is optional.
diff --git a/op-node/flags/flags.go b/op-node/flags/flags.go
index 5dcc78ddf..6bdda5172 100644
--- a/op-node/flags/flags.go
+++ b/op-node/flags/flags.go
@@ -244,6 +244,18 @@ var (
 		EnvVars: prefixEnvVars("BETA_EXTRA_NETWORKS"),
 		Hidden:  true,
 	}
+	BetaRollupHalt = &cli.StringFlag{
+		Name:    "beta.rollup.halt",
+		Usage:   "Beta feature: opt-in option to halt on incompatible protocol version requirements of the given level (major/minor/patch/none), as signaled onchain in L1",
+		EnvVars: prefixEnvVars("BETA_ROLLUP_HALT"),
+		Hidden:  true,
+	}
+	BetaRollupLoadProtocolVersions = &cli.BoolFlag{
+		Name:    "beta.rollup.load-protocol-versions",
+		Usage:   "Beta feature: load protocol versions from the superchain L1 ProtocolVersions contract (if available), and report in logs and metrics",
+		EnvVars: prefixEnvVars("BETA_ROLLUP_LOAD_PROTOCOL_VERSIONS"),
+		Hidden:  true,
+	}
 )
 
 var requiredFlags = []cli.Flag{
@@ -286,6 +298,8 @@ var optionalFlags = []cli.Flag{
 	L2EngineSyncEnabled,
 	SkipSyncStartCheck,
 	BetaExtraNetworks,
+	BetaRollupHalt,
+	BetaRollupLoadProtocolVersions,
 }
 
 // Flags contains the list of configuration options available to the binary.
diff --git a/op-node/metrics/metrics.go b/op-node/metrics/metrics.go
index c468ce6e5..57f8f1151 100644
--- a/op-node/metrics/metrics.go
+++ b/op-node/metrics/metrics.go
@@ -10,6 +10,8 @@ import (
 	"strconv"
 	"time"
 
+	"github.com/ethereum/go-ethereum/params"
+
 	"github.com/ethereum-optimism/optimism/op-node/p2p/store"
 	ophttp "github.com/ethereum-optimism/optimism/op-service/httputil"
 	"github.com/ethereum-optimism/optimism/op-service/metrics"
@@ -78,6 +80,7 @@ type Metricer interface {
 	RecordIPUnban()
 	RecordDial(allow bool)
 	RecordAccept(allow bool)
+	ReportProtocolVersions(local, engine, recommended, required params.ProtocolVersion)
 }
 
 // Metrics tracks all the metrics for the op-node.
@@ -153,6 +156,12 @@ type Metrics struct {
 
 	ChannelInputBytes prometheus.Counter
 
+	// Protocol version reporting
+	// Delta = params.ProtocolVersionComparison
+	ProtocolVersionDelta *prometheus.GaugeVec
+	// ProtocolVersions is pseudo-metric to report the exact protocol version info
+	ProtocolVersions *prometheus.GaugeVec
+
 	registry *prometheus.Registry
 	factory  metrics.Factory
 }
@@ -452,6 +461,24 @@ func NewMetrics(procName string) *Metrics {
 			Help:      "Number of sequencer block sealing jobs",
 		}),
 
+		ProtocolVersionDelta: factory.NewGaugeVec(prometheus.GaugeOpts{
+			Namespace: ns,
+			Name:      "protocol_version_delta",
+			Help:      "Difference between local and global protocol version, and execution-engine, per type of version",
+		}, []string{
+			"type",
+		}),
+		ProtocolVersions: factory.NewGaugeVec(prometheus.GaugeOpts{
+			Namespace: ns,
+			Name:      "protocol_versions",
+			Help:      "Pseudo-metric tracking recommended and required protocol version info",
+		}, []string{
+			"local",
+			"engine",
+			"recommended",
+			"required",
+		}),
+
 		registry: registry,
 		factory:  factory,
 	}
@@ -747,6 +774,13 @@ func (m *Metrics) RecordAccept(allow bool) {
 		m.Accepts.WithLabelValues("false").Inc()
 	}
 }
+func (m *Metrics) ReportProtocolVersions(local, engine, recommended, required params.ProtocolVersion) {
+	m.ProtocolVersionDelta.WithLabelValues("local_recommended").Set(float64(local.Compare(recommended)))
+	m.ProtocolVersionDelta.WithLabelValues("local_required").Set(float64(local.Compare(required)))
+	m.ProtocolVersionDelta.WithLabelValues("engine_recommended").Set(float64(engine.Compare(recommended)))
+	m.ProtocolVersionDelta.WithLabelValues("engine_required").Set(float64(engine.Compare(required)))
+	m.ProtocolVersions.WithLabelValues(local.String(), engine.String(), recommended.String(), required.String()).Set(1)
+}
 
 type noopMetricer struct{}
 
@@ -874,3 +908,5 @@ func (n *noopMetricer) RecordDial(allow bool) {
 
 func (n *noopMetricer) RecordAccept(allow bool) {
 }
+func (n *noopMetricer) ReportProtocolVersions(local, engine, recommended, required params.ProtocolVersion) {
+}
diff --git a/op-node/node/config.go b/op-node/node/config.go
index 8e79c21a3..4bb092cf5 100644
--- a/op-node/node/config.go
+++ b/op-node/node/config.go
@@ -42,7 +42,7 @@ type Config struct {
 	ConfigPersistence ConfigPersistence
 
 	// RuntimeConfigReloadInterval defines the interval between runtime config reloads.
-	// Disabled if 0.
+	// Disabled if <= 0.
 	// Runtime config changes should be picked up from log-events,
 	// but if log-events are not coming in (e.g. not syncing blocks) then the reload ensures the config stays accurate.
 	RuntimeConfigReloadInterval time.Duration
@@ -52,6 +52,10 @@ type Config struct {
 	Heartbeat HeartbeatConfig
 
 	Sync sync.Config
+
+	// To halt when detecting the node does not support a signaled protocol version
+	// change of the given severity (major/minor/patch). Disabled if empty.
+	RollupHalt string
 }
 
 type RPCConfig struct {
@@ -128,5 +132,8 @@ func (cfg *Config) Check() error {
 			return fmt.Errorf("p2p config error: %w", err)
 		}
 	}
+	if !(cfg.RollupHalt == "" || cfg.RollupHalt == "major" || cfg.RollupHalt == "minor" || cfg.RollupHalt == "patch") {
+		return fmt.Errorf("invalid rollup halting option: %q", cfg.RollupHalt)
+	}
 	return nil
 }
diff --git a/op-node/node/node.go b/op-node/node/node.go
index 91aad8121..740467fe6 100644
--- a/op-node/node/node.go
+++ b/op-node/node/node.go
@@ -2,7 +2,9 @@ package node
 
 import (
 	"context"
+	"errors"
 	"fmt"
+	"sync/atomic"
 	"time"
 
 	"github.com/hashicorp/go-multierror"
@@ -40,10 +42,14 @@ type OpNode struct {
 	tracer    Tracer                // tracer to get events for testing/debugging
 	runCfg    *RuntimeConfig        // runtime configurables
 
+	rollupHalt string // when to halt the rollup, disabled if empty
+
 	// some resources cannot be stopped directly, like the p2p gossipsub router (not our design),
 	// and depend on this ctx to be closed.
 	resourcesCtx   context.Context
 	resourcesClose context.CancelFunc
+
+	closed atomic.Bool
 }
 
 // The OpNode handles incoming gossip
@@ -58,6 +64,7 @@ func New(ctx context.Context, cfg *Config, log log.Logger, snapshotLog log.Logge
 		log:        log,
 		appVersion: appVersion,
 		metrics:    m,
+		rollupHalt: cfg.RollupHalt,
 	}
 	// not a context leak, gossipsub is closed with a context.
 	n.resourcesCtx, n.resourcesClose = context.WithCancel(context.Background())
@@ -189,6 +196,9 @@ func (n *OpNode) initRuntimeConfig(ctx context.Context, cfg *Config) error {
 			n.log.Error("failed to fetch runtime config data", "err", err)
 			return l1Head, err
 		}
+
+		n.handleProtocolVersionsUpdate(ctx)
+
 		return l1Head, nil
 	}
 
@@ -446,6 +456,10 @@ func (n *OpNode) RuntimeConfig() ReadonlyRuntimeConfig {
 
 // Close closes all resources.
 func (n *OpNode) Close() error {
+	if n.closed.Load() {
+		return errors.New("node is already closed")
+	}
+
 	var result *multierror.Error
 
 	if n.server != nil {
@@ -494,9 +508,18 @@ func (n *OpNode) Close() error {
 	if n.l1Source != nil {
 		n.l1Source.Close()
 	}
+
+	if result == nil { // mark as closed if we successfully fully closed
+		n.closed.Store(true)
+	}
+
 	return result.ErrorOrNil()
 }
 
+func (n *OpNode) Closed() bool {
+	return n.closed.Load()
+}
+
 func (n *OpNode) ListenAddr() string {
 	return n.server.listenAddr.String()
 }
diff --git a/op-node/node/runtime_config.go b/op-node/node/runtime_config.go
index 6be09b1a7..8e18a050c 100644
--- a/op-node/node/runtime_config.go
+++ b/op-node/node/runtime_config.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/log"
+	"github.com/ethereum/go-ethereum/params"
 
 	"github.com/ethereum-optimism/optimism/op-node/p2p"
 	"github.com/ethereum-optimism/optimism/op-node/rollup"
@@ -17,6 +18,14 @@ var (
 	// UnsafeBlockSignerAddressSystemConfigStorageSlot is the storage slot identifier of the unsafeBlockSigner
 	// `address` storage value in the SystemConfig L1 contract. Computed as `keccak256("systemconfig.unsafeblocksigner")`
 	UnsafeBlockSignerAddressSystemConfigStorageSlot = common.HexToHash("0x65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c08")
+
+	// RequiredProtocolVersionStorageSlot is the storage slot that the required protocol version is stored at.
+	// Computed as: `bytes32(uint256(keccak256("protocolversion.required")) - 1)`
+	RequiredProtocolVersionStorageSlot = common.HexToHash("0x4aaefe95bd84fd3f32700cf3b7566bc944b73138e41958b5785826df2aecace0")
+
+	// RecommendedProtocolVersionStorageSlot is the storage slot that the recommended protocol version is stored at.
+	// Computed as: `bytes32(uint256(keccak256("protocolversion.recommended")) - 1)`
+	RecommendedProtocolVersionStorageSlot = common.HexToHash("0xe314dfc40f0025322aacc0ba8ef420b62fb3b702cf01e0cdf3d829117ac2ff1a")
 )
 
 type RuntimeCfgL1Source interface {
@@ -25,6 +34,8 @@ type RuntimeCfgL1Source interface {
 
 type ReadonlyRuntimeConfig interface {
 	P2PSequencerAddress() common.Address
+	RequiredProtocolVersion() params.ProtocolVersion
+	RecommendedProtocolVersion() params.ProtocolVersion
 }
 
 // RuntimeConfig maintains runtime-configurable options.
@@ -49,6 +60,10 @@ type RuntimeConfig struct {
 // runtimeConfigData is a flat bundle of configurable data, easy and light to copy around.
 type runtimeConfigData struct {
 	p2pBlockSignerAddr common.Address
+
+	// superchain protocol version signals
+	recommended params.ProtocolVersion
+	required    params.ProtocolVersion
 }
 
 var _ p2p.GossipRuntimeConfig = (*RuntimeConfig)(nil)
@@ -67,18 +82,46 @@ func (r *RuntimeConfig) P2PSequencerAddress() common.Address {
 	return r.p2pBlockSignerAddr
 }
 
+func (r *RuntimeConfig) RequiredProtocolVersion() params.ProtocolVersion {
+	r.mu.RLock()
+	defer r.mu.RUnlock()
+	return r.required
+}
+
+func (r *RuntimeConfig) RecommendedProtocolVersion() params.ProtocolVersion {
+	r.mu.RLock()
+	defer r.mu.RUnlock()
+	return r.recommended
+}
+
 // Load resets the runtime configuration by fetching the latest config data from L1 at the given L1 block.
 // Load is safe to call concurrently, but will lock the runtime configuration modifications only,
 // and will thus not block other Load calls with possibly alternative L1 block views.
 func (r *RuntimeConfig) Load(ctx context.Context, l1Ref eth.L1BlockRef) error {
-	val, err := r.l1Client.ReadStorageAt(ctx, r.rollupCfg.L1SystemConfigAddress, UnsafeBlockSignerAddressSystemConfigStorageSlot, l1Ref.Hash)
+	p2pSignerVal, err := r.l1Client.ReadStorageAt(ctx, r.rollupCfg.L1SystemConfigAddress, UnsafeBlockSignerAddressSystemConfigStorageSlot, l1Ref.Hash)
 	if err != nil {
 		return fmt.Errorf("failed to fetch unsafe block signing address from system config: %w", err)
 	}
+	// The superchain protocol version data is optional; only applicable to rollup configs that specify a ProtocolVersions address.
+	var requiredProtVersion, recommendedProtoVersion params.ProtocolVersion
+	if r.rollupCfg.ProtocolVersionsAddress != (common.Address{}) {
+		requiredVal, err := r.l1Client.ReadStorageAt(ctx, r.rollupCfg.ProtocolVersionsAddress, RequiredProtocolVersionStorageSlot, l1Ref.Hash)
+		if err != nil {
+			return fmt.Errorf("required-protocol-version value failed to load from L1 contract: %w", err)
+		}
+		requiredProtVersion = params.ProtocolVersion(requiredVal)
+		recommendedVal, err := r.l1Client.ReadStorageAt(ctx, r.rollupCfg.ProtocolVersionsAddress, RecommendedProtocolVersionStorageSlot, l1Ref.Hash)
+		if err != nil {
+			return fmt.Errorf("recommended-protocol-version value failed to load from L1 contract: %w", err)
+		}
+		recommendedProtoVersion = params.ProtocolVersion(recommendedVal)
+	}
 	r.mu.Lock()
 	defer r.mu.Unlock()
 	r.l1Ref = l1Ref
-	r.p2pBlockSignerAddr = common.BytesToAddress(val[:])
+	r.p2pBlockSignerAddr = common.BytesToAddress(p2pSignerVal[:])
+	r.required = requiredProtVersion
+	r.recommended = recommendedProtoVersion
 	r.log.Info("loaded new runtime config values!", "p2p_seq_address", r.p2pBlockSignerAddr)
 	return nil
 }
diff --git a/op-node/node/superchain.go b/op-node/node/superchain.go
new file mode 100644
index 000000000..f7ec01376
--- /dev/null
+++ b/op-node/node/superchain.go
@@ -0,0 +1,72 @@
+package node
+
+import (
+	"context"
+
+	"github.com/ethereum-optimism/optimism/op-node/rollup"
+	"github.com/ethereum/go-ethereum/eth/catalyst"
+	"github.com/ethereum/go-ethereum/params"
+)
+
+func (n *OpNode) handleProtocolVersionsUpdate(ctx context.Context) {
+	recommended := n.runCfg.RecommendedProtocolVersion()
+	required := n.runCfg.RequiredProtocolVersion()
+	// if the protocol version sources are disabled we do not process them
+	if recommended == (params.ProtocolVersion{}) && required == (params.ProtocolVersion{}) {
+		return
+	}
+	local := rollup.OPStackSupport
+	// forward to execution engine, and get back the protocol version that op-geth supports
+	engineSupport, err := n.l2Source.SignalSuperchainV1(ctx, recommended, required)
+	if err != nil {
+		n.log.Warn("failed to notify engine of protocol version", "err", err)
+		// engineSupport may still be available, or otherwise zero to signal as unknown
+	} else {
+		catalyst.LogProtocolVersionSupport(n.log.New("node", "op-node"), engineSupport, recommended, "recommended")
+		catalyst.LogProtocolVersionSupport(n.log.New("node", "op-node"), engineSupport, required, "required")
+	}
+	n.metrics.ReportProtocolVersions(local, engineSupport, recommended, required)
+	catalyst.LogProtocolVersionSupport(n.log.New("node", "engine"), local, recommended, "recommended")
+	catalyst.LogProtocolVersionSupport(n.log.New("node", "engine"), local, required, "required")
+
+	// We may need to halt the node, if the user opted in to handling incompatible protocol-version signals
+	n.HaltMaybe()
+}
+
+// HaltMaybe halts the rollup node if the runtime config indicates an incompatible required protocol change
+// and the node is configured to opt-in to halting at this protocol-change level.
+func (n *OpNode) HaltMaybe() {
+	local := rollup.OPStackSupport
+	required := n.runCfg.RequiredProtocolVersion()
+	if haltMaybe(n.rollupHalt, local.Compare(required)) { // halt if we opted in to do so at this granularity
+		n.log.Error("Opted to halt, unprepared for protocol change", "required", required, "local", local)
+		if err := n.Close(); err != nil {
+			n.log.Error("Failed to halt rollup", "err", err)
+		}
+	}
+}
+
+// haltMaybe returns true when we should halt, given the halt-option and required-version comparison
+func haltMaybe(haltOption string, reqCmp params.ProtocolVersionComparison) bool {
+	var needLevel int
+	switch haltOption {
+	case "major":
+		needLevel = 3
+	case "minor":
+		needLevel = 2
+	case "patch":
+		needLevel = 1
+	default:
+		return false // do not consider halting if not configured to
+	}
+	haveLevel := 0
+	switch reqCmp {
+	case params.OutdatedMajor:
+		haveLevel = 3
+	case params.OutdatedMinor:
+		haveLevel = 2
+	case params.OutdatedPatch:
+		haveLevel = 1
+	}
+	return haveLevel >= needLevel
+}
diff --git a/op-node/rollup/superchain.go b/op-node/rollup/superchain.go
index 8db1df966..f07e1021a 100644
--- a/op-node/rollup/superchain.go
+++ b/op-node/rollup/superchain.go
@@ -4,12 +4,16 @@ import (
 	"fmt"
 	"math/big"
 
+	"github.com/ethereum/go-ethereum/params"
+
 	"github.com/ethereum/go-ethereum/common"
 
 	"github.com/ethereum-optimism/optimism/op-service/eth"
 	"github.com/ethereum-optimism/superchain-registry/superchain"
 )
 
+var OPStackSupport = params.ProtocolVersionV0{Build: [8]byte{}, Major: 3, Minor: 1, Patch: 0, PreRelease: 1}.Encode()
+
 const (
 	opMainnet   = 10
 	opGoerli    = 420
diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go
index dce6f5104..67d80361a 100644
--- a/op-node/rollup/types.go
+++ b/op-node/rollup/types.go
@@ -84,6 +84,9 @@ type Config struct {
 	DepositContractAddress common.Address `json:"deposit_contract_address"`
 	// L1 System Config Address
 	L1SystemConfigAddress common.Address `json:"l1_system_config_address"`
+
+	// L1 address that declares the protocol versions, optional (Beta feature)
+	ProtocolVersionsAddress common.Address `json:"protocol_versions_address,omitempty"`
 }
 
 // ValidateL1Config checks L1 config variables for errors.
@@ -283,6 +286,8 @@ func (c *Config) Description(l2Chains map[string]string) string {
 	// Report the upgrade configuration
 	banner += "Post-Bedrock Network Upgrades (timestamp based):\n"
 	banner += fmt.Sprintf("  - Regolith: %s\n", fmtForkTimeOrUnset(c.RegolithTime))
+	// Report the protocol version
+	banner += fmt.Sprintf("Node supports up to OP-Stack Protocol Version: %s\n", OPStackSupport)
 	return banner
 }
 
diff --git a/op-node/service.go b/op-node/service.go
index 21c230fce..40dd6418e 100644
--- a/op-node/service.go
+++ b/op-node/service.go
@@ -36,6 +36,11 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
 		return nil, err
 	}
 
+	if !ctx.Bool(flags.BetaRollupLoadProtocolVersions.Name) {
+		log.Info("Not opted in to ProtocolVersions signal loading, disabling ProtocolVersions contract now.")
+		rollupConfig.ProtocolVersionsAddress = common.Address{}
+	}
+
 	configPersistence := NewConfigPersistence(ctx)
 
 	driverConfig := NewDriverConfig(ctx)
@@ -61,6 +66,11 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
 
 	syncConfig := NewSyncConfig(ctx)
 
+	haltOption := ctx.String(flags.BetaRollupHalt.Name)
+	if haltOption == "none" {
+		haltOption = ""
+	}
+
 	cfg := &node.Config{
 		L1:     l1Endpoint,
 		L2:     l2Endpoint,
@@ -93,6 +103,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
 		},
 		ConfigPersistence: configPersistence,
 		Sync:              *syncConfig,
+		RollupHalt:        haltOption,
 	}
 
 	if err := cfg.LoadPersisted(log); err != nil {
diff --git a/op-node/sources/engine_client.go b/op-node/sources/engine_client.go
index 0b8b24ede..ef92a51f2 100644
--- a/op-node/sources/engine_client.go
+++ b/op-node/sources/engine_client.go
@@ -5,6 +5,9 @@ import (
 	"fmt"
 	"time"
 
+	"github.com/ethereum/go-ethereum/eth/catalyst"
+	"github.com/ethereum/go-ethereum/params"
+
 	"github.com/ethereum-optimism/optimism/op-node/client"
 	"github.com/ethereum-optimism/optimism/op-node/rollup"
 	"github.com/ethereum-optimism/optimism/op-node/sources/caching"
@@ -125,3 +128,12 @@ func (s *EngineClient) GetPayload(ctx context.Context, payloadId eth.PayloadID)
 	e.Trace("Received payload")
 	return &result, nil
 }
+
+func (s *EngineClient) SignalSuperchainV1(ctx context.Context, recommended, required params.ProtocolVersion) (params.ProtocolVersion, error) {
+	var result params.ProtocolVersion
+	err := s.client.CallContext(ctx, &result, "engine_signalSuperchainV1", &catalyst.SuperchainSignal{
+		Recommended: recommended,
+		Required:    required,
+	})
+	return result, err
+}
diff --git a/op-service/eth/account_proof.go b/op-service/eth/account_proof.go
index f0414de90..b7d09eeff 100644
--- a/op-service/eth/account_proof.go
+++ b/op-service/eth/account_proof.go
@@ -51,6 +51,9 @@ func (res *AccountResult) Verify(stateRoot common.Hash) error {
 		if err != nil {
 			return fmt.Errorf("failed to verify storage value %d with key %s (path %x) in storage trie %s: %w", i, entry.Key, path, res.StorageHash, err)
 		}
+		if val == nil && entry.Value.ToInt().Cmp(common.Big0) == 0 { // empty storage is zero by default
+			continue
+		}
 		comparison, err := rlp.EncodeToBytes(entry.Value.ToInt().Bytes())
 		if err != nil {
 			return fmt.Errorf("failed to encode storage value %d with key %s (path %x) in storage trie %s: %w", i, entry.Key, path, res.StorageHash, err)
diff --git a/op-service/ioutil/gzip.go b/op-service/ioutil/gzip.go
index 2c87125b6..412273e25 100644
--- a/op-service/ioutil/gzip.go
+++ b/op-service/ioutil/gzip.go
@@ -2,7 +2,6 @@ package ioutil
 
 import (
 	"compress/gzip"
-	"encoding/json"
 	"fmt"
 	"io"
 	"os"
@@ -39,20 +38,6 @@ func OpenCompressed(file string, flag int, perm os.FileMode) (io.WriteCloser, er
 	return out, nil
 }
 
-// WriteCompressedJson writes the object to the specified file as a compressed json object
-// if the filename ends with .gz.
-func WriteCompressedJson(file string, obj any) error {
-	if !IsGzip(file) {
-		return fmt.Errorf("file %v does not have .gz extension", file)
-	}
-	out, err := OpenCompressed(file, os.O_CREATE|os.O_WRONLY, 0644)
-	if err != nil {
-		return err
-	}
-	defer out.Close()
-	return json.NewEncoder(out).Encode(obj)
-}
-
 // IsGzip determines if a path points to a gzip compressed file.
 // Returns true when the file has a .gz extension.
 func IsGzip(path string) bool {
diff --git a/op-service/txmgr/txmgr.go b/op-service/txmgr/txmgr.go
index b613faa40..80f95a07e 100644
--- a/op-service/txmgr/txmgr.go
+++ b/op-service/txmgr/txmgr.go
@@ -211,8 +211,14 @@ func (m *SimpleTxManager) craftTx(ctx context.Context, candidate TxCandidate) (*
 	}
 	gasFeeCap := calcGasFeeCap(basefee, gasTipCap)
 
+	nonce, err := m.nextNonce(ctx)
+	if err != nil {
+		return nil, err
+	}
+
 	rawTx := &types.DynamicFeeTx{
 		ChainID:   m.chainID,
+		Nonce:     nonce,
 		To:        candidate.To,
 		GasTipCap: gasTipCap,
 		GasFeeCap: gasFeeCap,
@@ -241,13 +247,6 @@ func (m *SimpleTxManager) craftTx(ctx context.Context, candidate TxCandidate) (*
 		rawTx.Gas = gas
 	}
 
-	// Avoid bumping the nonce if the gas estimation fails.
-	nonce, err := m.nextNonce(ctx)
-	if err != nil {
-		return nil, err
-	}
-	rawTx.Nonce = nonce
-
 	ctx, cancel := context.WithTimeout(ctx, m.cfg.NetworkTimeout)
 	defer cancel()
 	return m.cfg.Signer(ctx, m.cfg.From, types.NewTx(rawTx))
diff --git a/package.json b/package.json
index 90dc29857..ed1cfc0ca 100644
--- a/package.json
+++ b/package.json
@@ -45,7 +45,7 @@
     "@types/mocha": "^10.0.1",
     "@types/node": "^20.5.3",
     "@typescript-eslint/eslint-plugin": "^6.7.0",
-    "@typescript-eslint/parser": "^6.7.0",
+    "@typescript-eslint/parser": "^5.60.1",
     "chai": "^4.2.0",
     "depcheck": "^1.4.3",
     "doctoc": "^2.2.0",
diff --git a/packages/contracts-bedrock/scripts/Deploy.s.sol b/packages/contracts-bedrock/scripts/Deploy.s.sol
index 428f97c99..53002b000 100644
--- a/packages/contracts-bedrock/scripts/Deploy.s.sol
+++ b/packages/contracts-bedrock/scripts/Deploy.s.sol
@@ -48,7 +48,7 @@ contract Deploy is Deployer {
     /// @notice The create2 salt used for deployment of the contract implementations.
     ///         Using this helps to reduce config across networks as the implementation
     ///         addresses will be the same across networks when deployed with create2.
-    bytes32 constant IMPL_SALT = keccak256(bytes("ether's phoenix"));
+    bytes32 constant IMPL_SALT = bytes32("ether's phoenix");
 
     /// @notice The name of the script, used to ensure the right deploy artifacts
     ///         are used.
diff --git a/packages/sdk/package.json b/packages/sdk/package.json
index 3607d4a19..b12a3ae52 100644
--- a/packages/sdk/package.json
+++ b/packages/sdk/package.json
@@ -54,7 +54,7 @@
     "mocha": "^10.2.0",
     "nyc": "^15.1.0",
     "ts-node": "^10.9.1",
-    "typedoc": "^0.25.1",
+    "typedoc": "^0.24.8",
     "typescript": "^5.1.6",
     "viem": "^1.6.0",
     "vitest": "^0.34.2",
diff --git a/packages/sdk/src/interfaces/types.ts b/packages/sdk/src/interfaces/types.ts
index b016f1efb..9fcf57eb8 100644
--- a/packages/sdk/src/interfaces/types.ts
+++ b/packages/sdk/src/interfaces/types.ts
@@ -15,7 +15,6 @@ import { IBridgeAdapter } from './bridge-adapter'
 export enum L1ChainID {
   MAINNET = 1,
   GOERLI = 5,
-  SEPOLIA = 11155111,
   HARDHAT_LOCAL = 31337,
   BEDROCK_LOCAL_DEVNET = 900,
 }
@@ -26,7 +25,6 @@ export enum L1ChainID {
 export enum L2ChainID {
   OPTIMISM = 10,
   OPTIMISM_GOERLI = 420,
-  OPTIMISM_SEPOLIA = 11155420,
   OPTIMISM_HARDHAT_LOCAL = 31337,
   OPTIMISM_HARDHAT_DEVNET = 17,
   OPTIMISM_BEDROCK_ALPHA_TESTNET = 28528,
diff --git a/packages/sdk/src/utils/chain-constants.ts b/packages/sdk/src/utils/chain-constants.ts
index 79327fa9a..8a3e2a6dd 100644
--- a/packages/sdk/src/utils/chain-constants.ts
+++ b/packages/sdk/src/utils/chain-constants.ts
@@ -2,62 +2,50 @@ import { predeploys } from '@eth-optimism/core-utils'
 import { ethers } from 'ethers'
 import portalArtifactsMainnet from '@eth-optimism/contracts-bedrock/deployments/mainnet/OptimismPortalProxy.json'
 import portalArtifactsGoerli from '@eth-optimism/contracts-bedrock/deployments/goerli/OptimismPortalProxy.json'
-import portalArtifactsSepolia from '@eth-optimism/contracts-bedrock/deployments/sepolia/OptimismPortalProxy.json'
 import l2OutputOracleArtifactsMainnet from '@eth-optimism/contracts-bedrock/deployments/mainnet/L2OutputOracleProxy.json'
 import l2OutputOracleArtifactsGoerli from '@eth-optimism/contracts-bedrock/deployments/goerli/L2OutputOracleProxy.json'
-import l2OutputOracleArtifactsSepolia from '@eth-optimism/contracts-bedrock/deployments/sepolia/L2OutputOracleProxy.json'
 import addressManagerArtifactMainnet from '@eth-optimism/contracts-bedrock/deployments/mainnet/AddressManager.json'
 import addressManagerArtifactGoerli from '@eth-optimism/contracts-bedrock/deployments/goerli/AddressManager.json'
-import addressManagerArtifactSepolia from '@eth-optimism/contracts-bedrock/deployments/sepolia/AddressManager.json'
 import l1StandardBridgeArtifactMainnet from '@eth-optimism/contracts-bedrock/deployments/mainnet/L1StandardBridgeProxy.json'
 import l1StandardBridgeArtifactGoerli from '@eth-optimism/contracts-bedrock/deployments/goerli/L1StandardBridgeProxy.json'
-import l1StandardBridgeArtifactSepolia from '@eth-optimism/contracts-bedrock/deployments/sepolia/L1StandardBridgeProxy.json'
 import l1CrossDomainMessengerArtifactMainnet from '@eth-optimism/contracts-bedrock/deployments/mainnet/L1CrossDomainMessengerProxy.json'
 import l1CrossDomainMessengerArtifactGoerli from '@eth-optimism/contracts-bedrock/deployments/goerli/L1CrossDomainMessengerProxy.json'
-import l1CrossDomainMessengerArtifactSepolia from '@eth-optimism/contracts-bedrock/deployments/sepolia/L1CrossDomainMessengerProxy.json'
 
 const portalAddresses = {
   mainnet: portalArtifactsMainnet.address,
   goerli: portalArtifactsGoerli.address,
-  sepolia: portalArtifactsSepolia.address,
 }
 
 const l2OutputOracleAddresses = {
   mainnet: l2OutputOracleArtifactsMainnet.address,
   goerli: l2OutputOracleArtifactsGoerli.address,
-  sepolia: l2OutputOracleArtifactsSepolia.address,
 }
 
 const addressManagerAddresses = {
   mainnet: addressManagerArtifactMainnet.address,
   goerli: addressManagerArtifactGoerli.address,
-  sepolia: addressManagerArtifactSepolia.address,
 }
 
 const l1StandardBridgeAddresses = {
   mainnet: l1StandardBridgeArtifactMainnet.address,
   goerli: l1StandardBridgeArtifactGoerli.address,
-  sepolia: l1StandardBridgeArtifactSepolia.address,
 }
 
 const l1CrossDomainMessengerAddresses = {
   mainnet: l1CrossDomainMessengerArtifactMainnet.address,
   goerli: l1CrossDomainMessengerArtifactGoerli.address,
-  sepolia: l1CrossDomainMessengerArtifactSepolia.address,
 }
 
 // legacy
 const stateCommitmentChainAddresses = {
   mainnet: '0xBe5dAb4A2e9cd0F27300dB4aB94BeE3A233AEB19',
   goerli: '0x9c945aC97Baf48cB784AbBB61399beB71aF7A378',
-  sepolia: ethers.constants.AddressZero,
 }
 
 // legacy
 const canonicalTransactionChainAddresses = {
   mainnet: '0x5E4e65926BA27467555EB562121fac00D24E9dD2',
   goerli: '0x607F755149cFEB3a14E1Dc3A4E2450Cde7dfb04D',
-  sepolia: ethers.constants.AddressZero,
 }
 
 import {
@@ -79,7 +67,6 @@ export const DEPOSIT_CONFIRMATION_BLOCKS: {
 } = {
   [L2ChainID.OPTIMISM]: 50 as const,
   [L2ChainID.OPTIMISM_GOERLI]: 12 as const,
-  [L2ChainID.OPTIMISM_SEPOLIA]: 12 as const,
   [L2ChainID.OPTIMISM_HARDHAT_LOCAL]: 2 as const,
   [L2ChainID.OPTIMISM_HARDHAT_DEVNET]: 2 as const,
   [L2ChainID.OPTIMISM_BEDROCK_ALPHA_TESTNET]: 12 as const,
@@ -94,7 +81,6 @@ export const CHAIN_BLOCK_TIMES: {
 } = {
   [L1ChainID.MAINNET]: 13 as const,
   [L1ChainID.GOERLI]: 15 as const,
-  [L1ChainID.SEPOLIA]: 15 as const,
   [L1ChainID.HARDHAT_LOCAL]: 1 as const,
   [L1ChainID.BEDROCK_LOCAL_DEVNET]: 15 as const,
 }
@@ -151,10 +137,6 @@ export const CONTRACT_ADDRESSES: {
     l1: getL1ContractsByNetworkName('goerli'),
     l2: DEFAULT_L2_CONTRACT_ADDRESSES,
   },
-  [L2ChainID.OPTIMISM_SEPOLIA]: {
-    l1: getL1ContractsByNetworkName('sepolia'),
-    l2: DEFAULT_L2_CONTRACT_ADDRESSES,
-  },
   [L2ChainID.OPTIMISM_HARDHAT_LOCAL]: {
     l1: {
       AddressManager: '0x5FbDB2315678afecb367f032d93F642f64180aa3' as const,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0ed8b2ba9..e56e7c83c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -17,7 +17,7 @@ importers:
     devDependencies:
       '@babel/eslint-parser':
         specifier: ^7.18.2
-        version: 7.22.15(@babel/core@7.22.10)(eslint@8.49.0)
+        version: 7.22.15(@babel/core@7.22.10)(eslint@8.47.0)
       '@changesets/changelog-github':
         specifier: ^0.4.8
         version: 0.4.8
@@ -35,55 +35,55 @@ importers:
         version: 10.0.1
       '@types/node':
         specifier: ^20.5.3
-        version: 20.6.0
+        version: 20.5.3
       '@typescript-eslint/eslint-plugin':
         specifier: ^6.7.0
-        version: 6.7.0(@typescript-eslint/parser@6.7.0)(eslint@8.49.0)(typescript@5.1.6)
+        version: 6.7.0(@typescript-eslint/parser@5.60.1)(eslint@8.47.0)(typescript@5.1.6)
       '@typescript-eslint/parser':
-        specifier: ^6.7.0
-        version: 6.7.0(eslint@8.49.0)(typescript@5.1.6)
+        specifier: ^5.60.1
+        version: 5.60.1(eslint@8.47.0)(typescript@5.1.6)
       chai:
         specifier: ^4.2.0
         version: 4.3.7
       depcheck:
         specifier: ^1.4.3
-        version: 1.4.6
+        version: 1.4.3
       doctoc:
         specifier: ^2.2.0
-        version: 2.2.1
+        version: 2.2.0
       eslint:
         specifier: ^8.43.0
-        version: 8.49.0
+        version: 8.47.0
       eslint-config-prettier:
         specifier: ^8.3.0
-        version: 8.3.0(eslint@8.49.0)
+        version: 8.3.0(eslint@8.47.0)
       eslint-config-standard:
         specifier: ^16.0.3
-        version: 16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.49.0)
+        version: 16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.47.0)
       eslint-plugin-import:
         specifier: ^2.26.0
-        version: 2.28.1(@typescript-eslint/parser@6.7.0)(eslint@8.49.0)
+        version: 2.28.1(@typescript-eslint/parser@5.60.1)(eslint@8.47.0)
       eslint-plugin-jsdoc:
         specifier: ^35.1.2
-        version: 35.5.1(eslint@8.49.0)
+        version: 35.5.1(eslint@8.47.0)
       eslint-plugin-node:
         specifier: ^11.1.0
-        version: 11.1.0(eslint@8.49.0)
+        version: 11.1.0(eslint@8.47.0)
       eslint-plugin-prefer-arrow:
         specifier: ^1.2.3
-        version: 1.2.3(eslint@8.49.0)
+        version: 1.2.3(eslint@8.47.0)
       eslint-plugin-prettier:
         specifier: ^4.0.0
-        version: 4.2.1(eslint-config-prettier@8.3.0)(eslint@8.49.0)(prettier@2.8.8)
+        version: 4.2.1(eslint-config-prettier@8.3.0)(eslint@8.47.0)(prettier@2.8.8)
       eslint-plugin-promise:
         specifier: ^5.1.0
-        version: 5.2.0(eslint@8.49.0)
+        version: 5.2.0(eslint@8.47.0)
       eslint-plugin-react:
         specifier: ^7.24.0
-        version: 7.33.2(eslint@8.49.0)
+        version: 7.33.2(eslint@8.47.0)
       eslint-plugin-unicorn:
         specifier: ^42.0.0
-        version: 42.0.0(eslint@8.49.0)
+        version: 42.0.0(eslint@8.47.0)
       husky:
         specifier: ^8.0.3
         version: 8.0.3
@@ -175,7 +175,7 @@ importers:
         version: 2.17.2(ts-node@10.9.1)(typescript@5.2.2)
       ts-node:
         specifier: ^10.9.1
-        version: 10.9.1(@types/node@20.6.0)(typescript@5.2.2)
+        version: 10.9.1(@types/node@20.5.3)(typescript@5.2.2)
       tsx:
         specifier: ^3.12.7
         version: 3.12.7
@@ -260,10 +260,10 @@ importers:
     devDependencies:
       '@typescript-eslint/eslint-plugin':
         specifier: ^6.7.0
-        version: 6.7.0(@typescript-eslint/parser@6.4.0)(eslint@8.49.0)(typescript@5.1.6)
+        version: 6.7.0(@typescript-eslint/parser@6.4.0)(eslint@8.47.0)(typescript@5.1.6)
       '@typescript-eslint/parser':
         specifier: ^6.4.0
-        version: 6.4.0(eslint@8.49.0)(typescript@5.1.6)
+        version: 6.4.0(eslint@8.47.0)(typescript@5.1.6)
       tsx:
         specifier: ^3.12.7
         version: 3.12.7
@@ -333,7 +333,7 @@ importers:
         version: 5.1.6
       vite:
         specifier: ^4.4.6
-        version: 4.4.6(@types/node@20.6.0)
+        version: 4.4.6(@types/node@20.5.3)
       vitest:
         specifier: ^0.34.2
         version: 0.34.2(jsdom@22.1.0)
@@ -427,7 +427,7 @@ importers:
         version: 1.3.1(typescript@5.1.6)
       vite:
         specifier: ^4.4.6
-        version: 4.4.6(@types/node@20.6.0)
+        version: 4.4.6(@types/node@20.5.3)
       vitest:
         specifier: ^0.34.2
         version: 0.34.2(jsdom@22.1.0)
@@ -508,8 +508,8 @@ importers:
         specifier: ^10.9.1
         version: 10.9.1(@types/node@20.5.0)(typescript@5.1.6)
       typedoc:
-        specifier: ^0.25.1
-        version: 0.25.1(typescript@5.1.6)
+        specifier: ^0.24.8
+        version: 0.24.8(typescript@5.1.6)
       typescript:
         specifier: ^5.1.6
         version: 5.1.6
@@ -558,7 +558,7 @@ importers:
         version: 1.6.0(typescript@5.1.6)(zod@3.22.0)
       vite:
         specifier: ^4.4.9
-        version: 4.4.9(@types/node@20.6.0)
+        version: 4.4.9(@types/node@20.5.3)
       vitest:
         specifier: ^0.34.1
         version: 0.34.1
@@ -623,7 +623,7 @@ packages:
       - supports-color
     dev: true
 
-  /@babel/eslint-parser@7.22.15(@babel/core@7.22.10)(eslint@8.49.0):
+  /@babel/eslint-parser@7.22.15(@babel/core@7.22.10)(eslint@8.47.0):
     resolution: {integrity: sha512-yc8OOBIQk1EcRrpizuARSQS0TWAcOMpEJ1aafhNznaeYkeL+OhqnDObGFylB8ka8VFF/sZc+S4RzHyO+3LjQxg==}
     engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
     peerDependencies:
@@ -632,7 +632,7 @@ packages:
     dependencies:
       '@babel/core': 7.22.10
       '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
-      eslint: 8.49.0
+      eslint: 8.47.0
       eslint-visitor-keys: 2.1.0
       semver: 6.3.1
     dev: true
@@ -751,16 +751,16 @@ packages:
       chalk: 2.4.2
       js-tokens: 4.0.0
 
-  /@babel/parser@7.22.10:
-    resolution: {integrity: sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==}
+  /@babel/parser@7.16.4:
+    resolution: {integrity: sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==}
     engines: {node: '>=6.0.0'}
     hasBin: true
     dependencies:
       '@babel/types': 7.22.10
     dev: true
 
-  /@babel/parser@7.22.5:
-    resolution: {integrity: sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==}
+  /@babel/parser@7.22.10:
+    resolution: {integrity: sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==}
     engines: {node: '>=6.0.0'}
     hasBin: true
     dependencies:
@@ -789,8 +789,8 @@ packages:
       '@babel/types': 7.22.10
     dev: true
 
-  /@babel/traverse@7.22.10:
-    resolution: {integrity: sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==}
+  /@babel/traverse@7.18.2:
+    resolution: {integrity: sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==}
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/code-frame': 7.22.10
@@ -807,8 +807,8 @@ packages:
       - supports-color
     dev: true
 
-  /@babel/traverse@7.22.5:
-    resolution: {integrity: sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==}
+  /@babel/traverse@7.22.10:
+    resolution: {integrity: sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==}
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/code-frame': 7.22.10
@@ -1591,13 +1591,13 @@ packages:
     dev: true
     optional: true
 
-  /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0):
+  /@eslint-community/eslint-utils@4.4.0(eslint@8.47.0):
     resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
     dependencies:
-      eslint: 8.49.0
+      eslint: 8.47.0
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -1623,8 +1623,8 @@ packages:
       - supports-color
     dev: true
 
-  /@eslint/js@8.49.0:
-    resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==}
+  /@eslint/js@8.47.0:
+    resolution: {integrity: sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
@@ -2222,8 +2222,8 @@ packages:
       '@trufflesuite/bigint-buffer': 1.1.9
     dev: true
 
-  /@humanwhocodes/config-array@0.11.11:
-    resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==}
+  /@humanwhocodes/config-array@0.11.10:
+    resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
     engines: {node: '>=10.10.0'}
     dependencies:
       '@humanwhocodes/object-schema': 1.2.1
@@ -3707,20 +3707,20 @@ packages:
   /@types/bn.js@4.11.6:
     resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/bn.js@5.1.0:
     resolution: {integrity: sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/body-parser@1.19.1:
     resolution: {integrity: sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==}
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/chai-as-promised@7.1.5:
@@ -3746,7 +3746,7 @@ packages:
   /@types/connect@3.4.35:
     resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
 
   /@types/dateformat@5.0.0:
     resolution: {integrity: sha512-SZg4JdHIWHQGEokbYGZSDvo5wA4TLYPXaqhigs/wH+REDOejcJzgH+qyY+HtEUtWOZxEUkbhbdYPqQDiEgrXeA==}
@@ -3760,7 +3760,7 @@ packages:
   /@types/express-serve-static-core@4.17.35:
     resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
       '@types/send': 0.17.1
@@ -3779,7 +3779,7 @@ packages:
     resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.6.0
+      '@types/node': 20.5.0
     dev: true
 
   /@types/is-ci@3.0.0:
@@ -3806,7 +3806,7 @@ packages:
     dependencies:
       '@types/abstract-leveldown': 5.0.2
       '@types/level-errors': 3.0.0
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/lru-cache@5.1.1:
@@ -3838,7 +3838,7 @@ packages:
   /@types/mkdirp@0.5.2:
     resolution: {integrity: sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/mocha@10.0.1:
@@ -3848,7 +3848,7 @@ packages:
   /@types/morgan@1.9.4:
     resolution: {integrity: sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.0
     dev: true
 
   /@types/ms@0.7.31:
@@ -3857,7 +3857,7 @@ packages:
   /@types/node-fetch@2.6.4:
     resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
       form-data: 3.0.1
     dev: true
 
@@ -3872,8 +3872,8 @@ packages:
     resolution: {integrity: sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==}
     dev: true
 
-  /@types/node@20.6.0:
-    resolution: {integrity: sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==}
+  /@types/node@20.5.3:
+    resolution: {integrity: sha512-ITI7rbWczR8a/S6qjAW7DMqxqFMjjTo61qZVWJ1ubPvbIQsL5D/TvwjYEalM8Kthpe3hTzOGrF2TGbAu2uyqeA==}
 
   /@types/normalize-package-data@2.4.1:
     resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
@@ -3885,7 +3885,7 @@ packages:
   /@types/pbkdf2@3.1.0:
     resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/pino-multi-stream@5.1.3:
@@ -3903,13 +3903,13 @@ packages:
   /@types/pino-std-serializers@2.4.1:
     resolution: {integrity: sha512-17XcksO47M24IVTVKPeAByWUd3Oez7EbIjXpSbzMPhXVzgjGtrOa49gKBwxH9hb8dKv58OelsWQ+A1G1l9S3wQ==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/pino@6.3.11:
     resolution: {integrity: sha512-S7+fLONqSpHeW9d7TApUqO6VN47KYgOXhCNKwGBVLHObq8HhaAYlVqUNdfnvoXjCMiwE5xcPm/5R2ZUh8bgaXQ==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
       '@types/pino-pretty': 4.7.1
       '@types/pino-std-serializers': 2.4.1
       sonic-boom: 2.8.0
@@ -3955,7 +3955,7 @@ packages:
   /@types/readable-stream@2.3.15:
     resolution: {integrity: sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
       safe-buffer: 5.1.2
     dev: true
 
@@ -3966,7 +3966,7 @@ packages:
   /@types/secp256k1@4.0.3:
     resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/seedrandom@3.0.1:
@@ -3985,14 +3985,14 @@ packages:
     resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
     dependencies:
       '@types/mime': 1.3.2
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/serve-static@1.13.10:
     resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==}
     dependencies:
       '@types/mime': 1.3.2
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
   /@types/sinon-chai@3.2.5:
@@ -4029,21 +4029,21 @@ packages:
   /@types/ws@7.4.7:
     resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
 
   /@types/ws@8.5.3:
     resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: false
 
   /@types/ws@8.5.5:
     resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==}
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
     dev: true
 
-  /@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.4.0)(eslint@8.49.0)(typescript@5.1.6):
+  /@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@5.60.1)(eslint@8.47.0)(typescript@5.1.6):
     resolution: {integrity: sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -4055,13 +4055,13 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.4.0(eslint@8.49.0)(typescript@5.1.6)
+      '@typescript-eslint/parser': 5.60.1(eslint@8.47.0)(typescript@5.1.6)
       '@typescript-eslint/scope-manager': 6.7.0
-      '@typescript-eslint/type-utils': 6.7.0(eslint@8.49.0)(typescript@5.1.6)
-      '@typescript-eslint/utils': 6.7.0(eslint@8.49.0)(typescript@5.1.6)
+      '@typescript-eslint/type-utils': 6.7.0(eslint@8.47.0)(typescript@5.1.6)
+      '@typescript-eslint/utils': 6.7.0(eslint@8.47.0)(typescript@5.1.6)
       '@typescript-eslint/visitor-keys': 6.7.0
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.47.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -4072,7 +4072,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.7.0)(eslint@8.49.0)(typescript@5.1.6):
+  /@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.4.0)(eslint@8.47.0)(typescript@5.1.6):
     resolution: {integrity: sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -4084,13 +4084,13 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.7.0(eslint@8.49.0)(typescript@5.1.6)
+      '@typescript-eslint/parser': 6.4.0(eslint@8.47.0)(typescript@5.1.6)
       '@typescript-eslint/scope-manager': 6.7.0
-      '@typescript-eslint/type-utils': 6.7.0(eslint@8.49.0)(typescript@5.1.6)
-      '@typescript-eslint/utils': 6.7.0(eslint@8.49.0)(typescript@5.1.6)
+      '@typescript-eslint/type-utils': 6.7.0(eslint@8.47.0)(typescript@5.1.6)
+      '@typescript-eslint/utils': 6.7.0(eslint@8.47.0)(typescript@5.1.6)
       '@typescript-eslint/visitor-keys': 6.7.0
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.47.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -4101,29 +4101,28 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.4.0(eslint@8.49.0)(typescript@5.1.6):
-    resolution: {integrity: sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@typescript-eslint/parser@5.60.1(eslint@8.47.0)(typescript@5.1.6):
+    resolution: {integrity: sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
+      eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/scope-manager': 6.4.0
-      '@typescript-eslint/types': 6.4.0
-      '@typescript-eslint/typescript-estree': 6.4.0(typescript@5.1.6)
-      '@typescript-eslint/visitor-keys': 6.4.0
+      '@typescript-eslint/scope-manager': 5.60.1
+      '@typescript-eslint/types': 5.60.1
+      '@typescript-eslint/typescript-estree': 5.60.1(typescript@5.1.6)
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.47.0
       typescript: 5.1.6
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.7.0(eslint@8.49.0)(typescript@5.1.6):
-    resolution: {integrity: sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==}
+  /@typescript-eslint/parser@6.4.0(eslint@8.47.0)(typescript@5.1.6):
+    resolution: {integrity: sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -4132,17 +4131,25 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/scope-manager': 6.7.0
-      '@typescript-eslint/types': 6.7.0
-      '@typescript-eslint/typescript-estree': 6.7.0(typescript@5.1.6)
-      '@typescript-eslint/visitor-keys': 6.7.0
+      '@typescript-eslint/scope-manager': 6.4.0
+      '@typescript-eslint/types': 6.4.0
+      '@typescript-eslint/typescript-estree': 6.4.0(typescript@5.1.6)
+      '@typescript-eslint/visitor-keys': 6.4.0
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.47.0
       typescript: 5.1.6
     transitivePeerDependencies:
       - supports-color
     dev: true
 
+  /@typescript-eslint/scope-manager@5.60.1:
+    resolution: {integrity: sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      '@typescript-eslint/types': 5.60.1
+      '@typescript-eslint/visitor-keys': 5.60.1
+    dev: true
+
   /@typescript-eslint/scope-manager@6.4.0:
     resolution: {integrity: sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -4159,7 +4166,7 @@ packages:
       '@typescript-eslint/visitor-keys': 6.7.0
     dev: true
 
-  /@typescript-eslint/type-utils@6.7.0(eslint@8.49.0)(typescript@5.1.6):
+  /@typescript-eslint/type-utils@6.7.0(eslint@8.47.0)(typescript@5.1.6):
     resolution: {integrity: sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -4170,15 +4177,20 @@ packages:
         optional: true
     dependencies:
       '@typescript-eslint/typescript-estree': 6.7.0(typescript@5.1.6)
-      '@typescript-eslint/utils': 6.7.0(eslint@8.49.0)(typescript@5.1.6)
+      '@typescript-eslint/utils': 6.7.0(eslint@8.47.0)(typescript@5.1.6)
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.47.0
       ts-api-utils: 1.0.1(typescript@5.1.6)
       typescript: 5.1.6
     transitivePeerDependencies:
       - supports-color
     dev: true
 
+  /@typescript-eslint/types@5.60.1:
+    resolution: {integrity: sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
+
   /@typescript-eslint/types@6.4.0:
     resolution: {integrity: sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -4189,6 +4201,27 @@ packages:
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
+  /@typescript-eslint/typescript-estree@5.60.1(typescript@5.1.6):
+    resolution: {integrity: sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/types': 5.60.1
+      '@typescript-eslint/visitor-keys': 5.60.1
+      debug: 4.3.4(supports-color@8.1.1)
+      globby: 11.1.0
+      is-glob: 4.0.3
+      semver: 7.5.4
+      tsutils: 3.21.0(typescript@5.1.6)
+      typescript: 5.1.6
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@typescript-eslint/typescript-estree@6.4.0(typescript@5.1.6):
     resolution: {integrity: sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -4231,25 +4264,33 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/utils@6.7.0(eslint@8.49.0)(typescript@5.1.6):
+  /@typescript-eslint/utils@6.7.0(eslint@8.47.0)(typescript@5.1.6):
     resolution: {integrity: sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.47.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.0
       '@typescript-eslint/scope-manager': 6.7.0
       '@typescript-eslint/types': 6.7.0
       '@typescript-eslint/typescript-estree': 6.7.0(typescript@5.1.6)
-      eslint: 8.49.0
+      eslint: 8.47.0
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
       - typescript
     dev: true
 
+  /@typescript-eslint/visitor-keys@5.60.1:
+    resolution: {integrity: sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      '@typescript-eslint/types': 5.60.1
+      eslint-visitor-keys: 3.4.3
+    dev: true
+
   /@typescript-eslint/visitor-keys@6.4.0:
     resolution: {integrity: sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -5353,10 +5394,10 @@ packages:
       json-schema-traverse: 0.4.1
       uri-js: 4.4.1
 
-  /anchor-markdown-header@0.6.0:
-    resolution: {integrity: sha512-v7HJMtE1X7wTpNFseRhxsY/pivP4uAJbidVhPT+yhz4i/vV1+qx371IXuV9V7bN6KjFtheLJxqaSm0Y/8neJTA==}
+  /anchor-markdown-header@0.5.7:
+    resolution: {integrity: sha512-AmikqcK15r3q99hPvTa1na9n3eLkW0uE+RL9BZMSgwYalQeDnNXbYrN06BIcBPfGlmsGIE2jvkuvl/x0hyPF5Q==}
     dependencies:
-      emoji-regex: 10.1.0
+      emoji-regex: 6.1.3
     dev: true
 
   /ansi-colors@4.1.1:
@@ -6025,10 +6066,6 @@ packages:
       function-bind: 1.1.1
       get-intrinsic: 1.2.1
 
-  /callsite@1.0.0:
-    resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==}
-    dev: true
-
   /callsites@3.1.0:
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
     engines: {node: '>=6'}
@@ -6784,32 +6821,32 @@ packages:
     resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
     engines: {node: '>=0.4.0'}
 
-  /depcheck@1.4.6:
-    resolution: {integrity: sha512-Jxy9+u1DE+Svj2N0V/ueEQiOgH2X3KRPAsBfM0m/vCtuiG5QSC//b1mt0rbN/u3BFFEzXqpHzYiwDjmvAydEsw==}
+  /depcheck@1.4.3:
+    resolution: {integrity: sha512-vy8xe1tlLFu7t4jFyoirMmOR7x7N601ubU9Gkifyr9z8rjBFtEdWHDBMqXyk6OkK+94NXutzddVXJuo0JlUQKQ==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
-      '@babel/parser': 7.22.5
-      '@babel/traverse': 7.22.5
+      '@babel/parser': 7.16.4
+      '@babel/traverse': 7.18.2
       '@vue/compiler-sfc': 3.2.36
-      callsite: 1.0.0
       camelcase: 6.3.0
       cosmiconfig: 7.0.1
       debug: 4.3.4(supports-color@8.1.1)
       deps-regex: 0.1.4
-      findup-sync: 5.0.0
       ignore: 5.2.4
-      is-core-module: 2.13.0
+      is-core-module: 2.12.1
       js-yaml: 3.14.1
       json5: 2.2.3
       lodash: 4.17.21
       minimatch: 3.1.2
       multimatch: 5.0.0
       please-upgrade-node: 3.2.0
+      query-ast: 1.0.4
       readdirp: 3.6.0
       require-package-name: 2.0.1
-      resolve: 1.22.4
-      resolve-from: 5.0.0
+      resolve: 1.22.2
+      sass: 1.52.1
+      scss-parser: 1.0.5
       semver: 7.5.4
       yargs: 16.2.0
     transitivePeerDependencies:
@@ -6837,11 +6874,6 @@ packages:
   /detect-browser@5.3.0:
     resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==}
 
-  /detect-file@1.0.0:
-    resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
   /detect-indent@6.1.0:
     resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
     engines: {node: '>=8'}
@@ -6889,14 +6921,14 @@ packages:
     dependencies:
       path-type: 4.0.0
 
-  /doctoc@2.2.1:
-    resolution: {integrity: sha512-qNJ1gsuo7hH40vlXTVVrADm6pdg30bns/Mo7Nv1SxuXSM1bwF9b4xQ40a6EFT/L1cI+Yylbyi8MPI4G4y7XJzQ==}
+  /doctoc@2.2.0:
+    resolution: {integrity: sha512-PtiyaS+S3kcMbpx6x2V0S+PeDKisxmjEFnZsuYkkj4Lh3ObozJuuYh9dM4+sX02Ouuty8RF2LOCnIbpu/hWy/A==}
     hasBin: true
     dependencies:
       '@textlint/markdown-to-ast': 12.2.1
-      anchor-markdown-header: 0.6.0
+      anchor-markdown-header: 0.5.7
       htmlparser2: 7.2.0
-      minimist: 1.2.8
+      minimist: 1.2.6
       underscore: 1.13.4
       update-section: 0.3.3
     transitivePeerDependencies:
@@ -7036,8 +7068,8 @@ packages:
     engines: {node: '>=12'}
     dev: true
 
-  /emoji-regex@10.1.0:
-    resolution: {integrity: sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg==}
+  /emoji-regex@6.1.3:
+    resolution: {integrity: sha512-73/zxHTjP2N2FQf0J5ngNjxP9LqG2krUshxYaowI8HxZQsiL2pYJc3k9/O93fc5/lCSkZv+bQ5Esk6k6msiSvg==}
     dev: true
 
   /emoji-regex@8.0.0:
@@ -7515,16 +7547,16 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
-  /eslint-config-prettier@8.3.0(eslint@8.49.0):
+  /eslint-config-prettier@8.3.0(eslint@8.47.0):
     resolution: {integrity: sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==}
     hasBin: true
     peerDependencies:
       eslint: '>=7.0.0'
     dependencies:
-      eslint: 8.49.0
+      eslint: 8.47.0
     dev: true
 
-  /eslint-config-standard@16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.49.0):
+  /eslint-config-standard@16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.47.0):
     resolution: {integrity: sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==}
     peerDependencies:
       eslint: ^7.12.1
@@ -7532,10 +7564,10 @@ packages:
       eslint-plugin-node: ^11.1.0
       eslint-plugin-promise: ^4.2.1 || ^5.0.0
     dependencies:
-      eslint: 8.49.0
-      eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.0)(eslint@8.49.0)
-      eslint-plugin-node: 11.1.0(eslint@8.49.0)
-      eslint-plugin-promise: 5.2.0(eslint@8.49.0)
+      eslint: 8.47.0
+      eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.60.1)(eslint@8.47.0)
+      eslint-plugin-node: 11.1.0(eslint@8.47.0)
+      eslint-plugin-promise: 5.2.0(eslint@8.47.0)
     dev: true
 
   /eslint-import-resolver-node@0.3.9:
@@ -7548,7 +7580,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0):
+  /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.60.1)(eslint-import-resolver-node@0.3.9)(eslint@8.47.0):
     resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -7569,26 +7601,26 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.7.0(eslint@8.49.0)(typescript@5.1.6)
+      '@typescript-eslint/parser': 5.60.1(eslint@8.47.0)(typescript@5.1.6)
       debug: 3.2.7
-      eslint: 8.49.0
+      eslint: 8.47.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /eslint-plugin-es@3.0.1(eslint@8.49.0):
+  /eslint-plugin-es@3.0.1(eslint@8.47.0):
     resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==}
     engines: {node: '>=8.10.0'}
     peerDependencies:
       eslint: '>=4.19.1'
     dependencies:
-      eslint: 8.49.0
+      eslint: 8.47.0
       eslint-utils: 2.1.0
       regexpp: 3.2.0
     dev: true
 
-  /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.0)(eslint@8.49.0):
+  /eslint-plugin-import@2.28.1(@typescript-eslint/parser@5.60.1)(eslint@8.47.0):
     resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -7598,16 +7630,16 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.7.0(eslint@8.49.0)(typescript@5.1.6)
+      '@typescript-eslint/parser': 5.60.1(eslint@8.47.0)(typescript@5.1.6)
       array-includes: 3.1.6
       array.prototype.findlastindex: 1.2.2
       array.prototype.flat: 1.3.1
       array.prototype.flatmap: 1.3.1
       debug: 3.2.7
       doctrine: 2.1.0
-      eslint: 8.49.0
+      eslint: 8.47.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.60.1)(eslint-import-resolver-node@0.3.9)(eslint@8.47.0)
       has: 1.0.3
       is-core-module: 2.13.0
       is-glob: 4.0.3
@@ -7623,7 +7655,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-jsdoc@35.5.1(eslint@8.49.0):
+  /eslint-plugin-jsdoc@35.5.1(eslint@8.47.0):
     resolution: {integrity: sha512-pPYPWtsykwVEue1tYEyoppBj4dgF7XicF67tLLLraY6RQYBq7qMKjUHji19+hfiTtYKKBD0YfeK8hgjPAE5viw==}
     engines: {node: '>=12'}
     peerDependencies:
@@ -7632,7 +7664,7 @@ packages:
       '@es-joy/jsdoccomment': 0.9.0-alpha.1
       comment-parser: 1.1.6-beta.0
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.47.0
       esquery: 1.4.0
       jsdoc-type-pratt-parser: 1.1.1
       lodash: 4.17.21
@@ -7643,14 +7675,14 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-node@11.1.0(eslint@8.49.0):
+  /eslint-plugin-node@11.1.0(eslint@8.47.0):
     resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==}
     engines: {node: '>=8.10.0'}
     peerDependencies:
       eslint: '>=5.16.0'
     dependencies:
-      eslint: 8.49.0
-      eslint-plugin-es: 3.0.1(eslint@8.49.0)
+      eslint: 8.47.0
+      eslint-plugin-es: 3.0.1(eslint@8.47.0)
       eslint-utils: 2.1.0
       ignore: 5.2.4
       minimatch: 3.1.2
@@ -7658,15 +7690,15 @@ packages:
       semver: 6.3.1
     dev: true
 
-  /eslint-plugin-prefer-arrow@1.2.3(eslint@8.49.0):
+  /eslint-plugin-prefer-arrow@1.2.3(eslint@8.47.0):
     resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==}
     peerDependencies:
       eslint: '>=2.0.0'
     dependencies:
-      eslint: 8.49.0
+      eslint: 8.47.0
     dev: true
 
-  /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.3.0)(eslint@8.49.0)(prettier@2.8.8):
+  /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.3.0)(eslint@8.47.0)(prettier@2.8.8):
     resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==}
     engines: {node: '>=12.0.0'}
     peerDependencies:
@@ -7677,22 +7709,22 @@ packages:
       eslint-config-prettier:
         optional: true
     dependencies:
-      eslint: 8.49.0
-      eslint-config-prettier: 8.3.0(eslint@8.49.0)
+      eslint: 8.47.0
+      eslint-config-prettier: 8.3.0(eslint@8.47.0)
       prettier: 2.8.8
       prettier-linter-helpers: 1.0.0
     dev: true
 
-  /eslint-plugin-promise@5.2.0(eslint@8.49.0):
+  /eslint-plugin-promise@5.2.0(eslint@8.47.0):
     resolution: {integrity: sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==}
     engines: {node: ^10.12.0 || >=12.0.0}
     peerDependencies:
       eslint: ^7.0.0
     dependencies:
-      eslint: 8.49.0
+      eslint: 8.47.0
     dev: true
 
-  /eslint-plugin-react@7.33.2(eslint@8.49.0):
+  /eslint-plugin-react@7.33.2(eslint@8.47.0):
     resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -7703,7 +7735,7 @@ packages:
       array.prototype.tosorted: 1.1.1
       doctrine: 2.1.0
       es-iterator-helpers: 1.0.13
-      eslint: 8.49.0
+      eslint: 8.47.0
       estraverse: 5.3.0
       jsx-ast-utils: 3.2.0
       minimatch: 3.1.2
@@ -7717,7 +7749,7 @@ packages:
       string.prototype.matchall: 4.0.8
     dev: true
 
-  /eslint-plugin-unicorn@42.0.0(eslint@8.49.0):
+  /eslint-plugin-unicorn@42.0.0(eslint@8.47.0):
     resolution: {integrity: sha512-ixBsbhgWuxVaNlPTT8AyfJMlhyC5flCJFjyK3oKE8TRrwBnaHvUbuIkCM1lqg8ryYrFStL/T557zfKzX4GKSlg==}
     engines: {node: '>=12'}
     peerDependencies:
@@ -7726,8 +7758,8 @@ packages:
       '@babel/helper-validator-identifier': 7.16.7
       ci-info: 3.8.0
       clean-regexp: 1.0.0
-      eslint: 8.49.0
-      eslint-utils: 3.0.0(eslint@8.49.0)
+      eslint: 8.47.0
+      eslint-utils: 3.0.0(eslint@8.47.0)
       esquery: 1.4.0
       indent-string: 4.0.0
       is-builtin-module: 3.1.0
@@ -7763,13 +7795,13 @@ packages:
       eslint-visitor-keys: 1.3.0
     dev: true
 
-  /eslint-utils@3.0.0(eslint@8.49.0):
+  /eslint-utils@3.0.0(eslint@8.47.0):
     resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
     engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
     peerDependencies:
       eslint: '>=5'
     dependencies:
-      eslint: 8.49.0
+      eslint: 8.47.0
       eslint-visitor-keys: 2.1.0
     dev: true
 
@@ -7788,16 +7820,16 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
-  /eslint@8.49.0:
-    resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==}
+  /eslint@8.47.0:
+    resolution: {integrity: sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     hasBin: true
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.47.0)
       '@eslint-community/regexpp': 4.6.2
       '@eslint/eslintrc': 2.1.2
-      '@eslint/js': 8.49.0
-      '@humanwhocodes/config-array': 0.11.11
+      '@eslint/js': 8.47.0
+      '@humanwhocodes/config-array': 0.11.10
       '@humanwhocodes/module-importer': 1.0.1
       '@nodelib/fs.walk': 1.2.8
       ajv: 6.12.6
@@ -8197,13 +8229,6 @@ packages:
       strip-final-newline: 3.0.0
     dev: true
 
-  /expand-tilde@2.0.2:
-    resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      homedir-polyfill: 1.0.3
-    dev: true
-
   /express-prom-bundle@6.6.0(prom-client@14.2.0):
     resolution: {integrity: sha512-tZh2P2p5a8/yxQ5VbRav011Poa4R0mHqdFwn9Swe/obXDe5F0jY9wtRAfNYnqk4LXY7akyvR/nrvAHxQPWUjsQ==}
     engines: {node: '>=10'}
@@ -8454,16 +8479,6 @@ packages:
       micromatch: 4.0.5
     dev: true
 
-  /findup-sync@5.0.0:
-    resolution: {integrity: sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==}
-    engines: {node: '>= 10.13.0'}
-    dependencies:
-      detect-file: 1.0.0
-      is-glob: 4.0.3
-      micromatch: 4.0.5
-      resolve-dir: 1.0.1
-    dev: true
-
   /flat-cache@3.0.4:
     resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
     engines: {node: ^10.12.0 || >=12.0.0}
@@ -8809,18 +8824,6 @@ packages:
       path-scurry: 1.10.1
     dev: true
 
-  /glob@10.3.4:
-    resolution: {integrity: sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    hasBin: true
-    dependencies:
-      foreground-child: 3.1.1
-      jackspeak: 2.2.1
-      minimatch: 9.0.3
-      minipass: 7.0.3
-      path-scurry: 1.10.1
-    dev: true
-
   /glob@7.1.4:
     resolution: {integrity: sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==}
     dependencies:
@@ -8875,26 +8878,6 @@ packages:
       once: 1.4.0
       path-is-absolute: 1.0.1
 
-  /global-modules@1.0.0:
-    resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      global-prefix: 1.0.2
-      is-windows: 1.0.2
-      resolve-dir: 1.0.1
-    dev: true
-
-  /global-prefix@1.0.2:
-    resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      expand-tilde: 2.0.2
-      homedir-polyfill: 1.0.3
-      ini: 1.3.8
-      is-windows: 1.0.2
-      which: 1.3.1
-    dev: true
-
   /globals@11.12.0:
     resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
     engines: {node: '>=4'}
@@ -9126,7 +9109,7 @@ packages:
       solc: 0.7.3(debug@4.3.4)
       source-map-support: 0.5.21
       stacktrace-parser: 0.1.10
-      ts-node: 10.9.1(@types/node@20.6.0)(typescript@5.2.2)
+      ts-node: 10.9.1(@types/node@20.5.3)(typescript@5.2.2)
       tsort: 0.0.1
       typescript: 5.2.2
       undici: 5.24.0
@@ -9229,13 +9212,6 @@ packages:
       minimalistic-assert: 1.0.1
       minimalistic-crypto-utils: 1.0.1
 
-  /homedir-polyfill@1.0.3:
-    resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      parse-passwd: 1.0.0
-    dev: true
-
   /hosted-git-info@2.8.9:
     resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
 
@@ -9397,10 +9373,6 @@ packages:
   /inherits@2.0.4:
     resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
 
-  /ini@1.3.8:
-    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
-    dev: true
-
   /internal-slot@1.0.5:
     resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
     engines: {node: '>= 0.4'}
@@ -9409,6 +9381,12 @@ packages:
       has: 1.0.3
       side-channel: 1.0.4
 
+  /invariant@2.2.4:
+    resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
+    dependencies:
+      loose-envify: 1.4.0
+    dev: true
+
   /invert-kv@1.0.0:
     resolution: {integrity: sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==}
     engines: {node: '>=0.10.0'}
@@ -9502,6 +9480,12 @@ packages:
       ci-info: 3.8.0
     dev: false
 
+  /is-core-module@2.12.1:
+    resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==}
+    dependencies:
+      has: 1.0.3
+    dev: true
+
   /is-core-module@2.13.0:
     resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==}
     dependencies:
@@ -11067,6 +11051,10 @@ packages:
       kind-of: 6.0.3
     dev: false
 
+  /minimist@1.2.6:
+    resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
+    dev: true
+
   /minimist@1.2.8:
     resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
 
@@ -11836,11 +11824,6 @@ packages:
       json-parse-even-better-errors: 2.3.1
       lines-and-columns: 1.2.4
 
-  /parse-passwd@1.0.0:
-    resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
   /parse5@7.1.2:
     resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
     dependencies:
@@ -12337,6 +12320,13 @@ packages:
     resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
     engines: {node: '>=0.6'}
 
+  /query-ast@1.0.4:
+    resolution: {integrity: sha512-KFJFSvODCBjIH5HbHvITj9EEZKYUU6VX0T5CuB1ayvjUoUaZkKMi6eeby5Tf8DMukyZHlJQOE1+f3vevKUe6eg==}
+    dependencies:
+      invariant: 2.2.4
+      lodash: 4.17.21
+    dev: true
+
   /query-string@6.14.1:
     resolution: {integrity: sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==}
     engines: {node: '>=6'}
@@ -12711,14 +12701,6 @@ packages:
     resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
     dev: true
 
-  /resolve-dir@1.0.1:
-    resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      expand-tilde: 2.0.2
-      global-modules: 1.0.0
-    dev: true
-
   /resolve-from@4.0.0:
     resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
     engines: {node: '>=4'}
@@ -12807,7 +12789,7 @@ packages:
     engines: {node: '>=14'}
     hasBin: true
     dependencies:
-      glob: 10.3.4
+      glob: 10.3.3
     dev: true
 
   /ripemd160@2.0.2:
@@ -12920,6 +12902,16 @@ packages:
   /safer-buffer@2.1.2:
     resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
 
+  /sass@1.52.1:
+    resolution: {integrity: sha512-fSzYTbr7z8oQnVJ3Acp9hV80dM1fkMN7mSD/25mpcct9F7FPBMOI8krEYALgU1aZoqGhQNhTPsuSmxjnIvAm4Q==}
+    engines: {node: '>=12.0.0'}
+    hasBin: true
+    dependencies:
+      chokidar: 3.5.3
+      immutable: 4.1.0
+      source-map-js: 1.0.2
+    dev: true
+
   /saxes@6.0.0:
     resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
     engines: {node: '>=v12.22.7'}
@@ -12935,6 +12927,14 @@ packages:
   /scrypt-js@3.0.1:
     resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==}
 
+  /scss-parser@1.0.5:
+    resolution: {integrity: sha512-RZOtvCmCnwkDo7kdcYBi807Y5EoTIxJ34AgEgJNDmOH1jl0/xG0FyYZFbH6Ga3Iwu7q8LSdxJ4C5UkzNXjQxKQ==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      invariant: 2.2.4
+      lodash: 4.17.21
+    dev: true
+
   /secp256k1@4.0.3:
     resolution: {integrity: sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==}
     engines: {node: '>=10.0.0'}
@@ -13889,7 +13889,7 @@ packages:
       yn: 3.1.1
     dev: true
 
-  /ts-node@10.9.1(@types/node@20.6.0)(typescript@5.2.2):
+  /ts-node@10.9.1(@types/node@20.5.3)(typescript@5.2.2):
     resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
     hasBin: true
     peerDependencies:
@@ -13908,7 +13908,7 @@ packages:
       '@tsconfig/node12': 1.0.11
       '@tsconfig/node14': 1.0.3
       '@tsconfig/node16': 1.0.4
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
       acorn: 8.10.0
       acorn-walk: 8.2.0
       arg: 4.1.3
@@ -14056,6 +14056,16 @@ packages:
       - ts-node
     dev: true
 
+  /tsutils@3.21.0(typescript@5.1.6):
+    resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
+    engines: {node: '>= 6'}
+    peerDependencies:
+      typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
+    dependencies:
+      tslib: 1.14.1
+      typescript: 5.1.6
+    dev: true
+
   /tsx@3.12.7:
     resolution: {integrity: sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw==}
     hasBin: true
@@ -14230,12 +14240,12 @@ packages:
     dependencies:
       is-typedarray: 1.0.0
 
-  /typedoc@0.25.1(typescript@5.1.6):
-    resolution: {integrity: sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==}
-    engines: {node: '>= 16'}
+  /typedoc@0.24.8(typescript@5.1.6):
+    resolution: {integrity: sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==}
+    engines: {node: '>= 14.14'}
     hasBin: true
     peerDependencies:
-      typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x
+      typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x
     dependencies:
       lunr: 2.3.9
       marked: 4.3.0
@@ -14600,7 +14610,7 @@ packages:
       - zod
     dev: true
 
-  /vite-node@0.34.1(@types/node@20.6.0):
+  /vite-node@0.34.1(@types/node@20.5.0):
     resolution: {integrity: sha512-odAZAL9xFMuAg8aWd7nSPT+hU8u2r9gU3LRm9QKjxBEF2rRdWpMuqkrkjvyVQEdNFiBctqr2Gg4uJYizm5Le6w==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
@@ -14610,7 +14620,7 @@ packages:
       mlly: 1.4.0
       pathe: 1.1.1
       picocolors: 1.0.0
-      vite: 4.4.9(@types/node@20.6.0)
+      vite: 4.4.9(@types/node@20.5.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -14622,7 +14632,7 @@ packages:
       - terser
     dev: true
 
-  /vite-node@0.34.2(@types/node@20.6.0):
+  /vite-node@0.34.2(@types/node@20.5.0):
     resolution: {integrity: sha512-JtW249Zm3FB+F7pQfH56uWSdlltCo1IOkZW5oHBzeQo0iX4jtC7o1t9aILMGd9kVekXBP2lfJBEQt9rBh07ebA==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
@@ -14632,7 +14642,7 @@ packages:
       mlly: 1.4.0
       pathe: 1.1.1
       picocolors: 1.0.0
-      vite: 4.4.9(@types/node@20.6.0)
+      vite: 4.4.9(@types/node@20.5.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -14644,7 +14654,7 @@ packages:
       - terser
     dev: true
 
-  /vite@4.4.6(@types/node@20.6.0):
+  /vite@4.4.6(@types/node@20.5.3):
     resolution: {integrity: sha512-EY6Mm8vJ++S3D4tNAckaZfw3JwG3wa794Vt70M6cNJ6NxT87yhq7EC8Rcap3ahyHdo8AhCmV9PTk+vG1HiYn1A==}
     engines: {node: ^14.18.0 || >=16.0.0}
     hasBin: true
@@ -14672,7 +14682,7 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
       esbuild: 0.18.15
       postcss: 8.4.27
       rollup: 3.26.3
@@ -14680,7 +14690,43 @@ packages:
       fsevents: 2.3.2
     dev: true
 
-  /vite@4.4.9(@types/node@20.6.0):
+  /vite@4.4.9(@types/node@20.5.0):
+    resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': '>= 14'
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+    dependencies:
+      '@types/node': 20.5.0
+      esbuild: 0.18.15
+      postcss: 8.4.27
+      rollup: 3.28.0
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+
+  /vite@4.4.9(@types/node@20.5.3):
     resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
     engines: {node: ^14.18.0 || >=16.0.0}
     hasBin: true
@@ -14708,7 +14754,7 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.6.0
+      '@types/node': 20.5.3
       esbuild: 0.18.15
       postcss: 8.4.27
       rollup: 3.28.0
@@ -14747,9 +14793,9 @@ packages:
       webdriverio:
         optional: true
     dependencies:
-      '@types/chai': 4.3.6
+      '@types/chai': 4.3.5
       '@types/chai-subset': 1.3.3
-      '@types/node': 20.6.0
+      '@types/node': 20.5.0
       '@vitest/expect': 0.34.1
       '@vitest/runner': 0.34.1
       '@vitest/snapshot': 0.34.1
@@ -14758,7 +14804,7 @@ packages:
       acorn: 8.10.0
       acorn-walk: 8.2.0
       cac: 6.7.14
-      chai: 4.3.8
+      chai: 4.3.7
       debug: 4.3.4(supports-color@8.1.1)
       local-pkg: 0.4.3
       magic-string: 0.30.1
@@ -14768,8 +14814,8 @@ packages:
       strip-literal: 1.0.1
       tinybench: 2.5.0
       tinypool: 0.7.0
-      vite: 4.4.9(@types/node@20.6.0)
-      vite-node: 0.34.1(@types/node@20.6.0)
+      vite: 4.4.9(@types/node@20.5.0)
+      vite-node: 0.34.1(@types/node@20.5.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -14812,9 +14858,9 @@ packages:
       webdriverio:
         optional: true
     dependencies:
-      '@types/chai': 4.3.6
+      '@types/chai': 4.3.5
       '@types/chai-subset': 1.3.3
-      '@types/node': 20.6.0
+      '@types/node': 20.5.0
       '@vitest/expect': 0.34.2
       '@vitest/runner': 0.34.2
       '@vitest/snapshot': 0.34.2
@@ -14823,7 +14869,7 @@ packages:
       acorn: 8.10.0
       acorn-walk: 8.2.0
       cac: 6.7.14
-      chai: 4.3.8
+      chai: 4.3.7
       debug: 4.3.4(supports-color@8.1.1)
       jsdom: 22.1.0
       local-pkg: 0.4.3
@@ -14834,8 +14880,8 @@ packages:
       strip-literal: 1.0.1
       tinybench: 2.5.0
       tinypool: 0.7.0
-      vite: 4.4.9(@types/node@20.6.0)
-      vite-node: 0.34.2(@types/node@20.6.0)
+      vite: 4.4.9(@types/node@20.5.0)
+      vite-node: 0.34.2(@types/node@20.5.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -15368,6 +15414,7 @@ packages:
     hasBin: true
     dependencies:
       isexe: 2.0.0
+    dev: false
 
   /which@2.0.2:
     resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 1162641e6..c324c78f8 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -2,4 +2,3 @@ packages:
   - 'packages/*'
   - 'endpoint-monitor'
   - 'op-exporter'
-  - 'indexer/ts'
diff --git a/specs/README.md b/specs/README.md
index fd6c570e3..ce4a5f2b6 100644
--- a/specs/README.md
+++ b/specs/README.md
@@ -15,7 +15,7 @@ that maintains 1:1 compatibility with Ethereum.
 - [Rollup Node](rollup-node.md)
 - [Rollup Node P2p](rollup-node-p2p.md)
 - [L2 Chain Derivation](derivation.md)
-- [Superchain Upgrades](superchain-upgrades.md)
+- [Network Upgrades](network-upgrades.md)
 - [System Config](system_config.md)
 - [Batch Submitter](batcher.md)
 - [Guaranteed Gas Market](guaranteed-gas-market.md)
diff --git a/specs/exec-engine.md b/specs/exec-engine.md
index 036e3a219..9730ee5bc 100644
--- a/specs/exec-engine.md
+++ b/specs/exec-engine.md
@@ -16,7 +16,6 @@
     - [Extended PayloadAttributesV1](#extended-payloadattributesv1)
   - [`engine_newPayloadV1`](#engine_newpayloadv1)
   - [`engine_getPayloadV1`](#engine_getpayloadv1)
-  - [`engine_signalSuperchainV1`](#engine_signalsuperchainv1)
 - [Networking](#networking)
 - [Sync](#sync)
   - [Happy-path sync](#happy-path-sync)
@@ -199,37 +198,6 @@ Applies a L2 block to the engine state.
 No modifications to [`engine_getPayloadV1`][engine_getPayloadV1].
 Retrieves a payload by ID, prepared by `engine_forkchoiceUpdatedV1` when called with `payloadAttributes`.
 
-### `engine_signalSuperchainV1`
-
-Optional extension to the Engine API. Signals superchain information to the Engine:
-V1 signals which protocol version is recommended and required.
-
-Types:
-
-```javascript
-SuperchainSignal: {
-    recommended: ProtocolVersion
-    required: ProtocolVersion
-}
-```
-
-`ProtocolVersion`: encoded for RPC as defined in
-[Protocol Version format specification](./superchain-upgrades.md#protocol-version-format).
-
-Parameters:
-
-- `signal`: `SuperchainSignal`, the signaled superchain information.
-
-Returns:
-
-- `ProtocolVersion`: the latest supported OP-Stack protocol version of the execution engine.
-
-The execution engine SHOULD warn the user when the recommended version is newer than
-the current version supported by the execution engine.
-
-The execution engine SHOULD take safety precautions if it does not meet the required protocol version.
-This may include halting the engine, with consent of the execution engine operator.
-
 ## Networking
 
 The execution engine can acquire all data through the rollup node, as derived from L1:
diff --git a/specs/network-upgrades.md b/specs/network-upgrades.md
new file mode 100644
index 000000000..82e906238
--- /dev/null
+++ b/specs/network-upgrades.md
@@ -0,0 +1,89 @@
+# Network Upgrades
+
+Network upgrades, also known as forks or hardforks, implement consensus-breaking changes.
+These changes are transitioned into deterministically across all nodes through an activation rule.
+
+This document lists the network upgrades of the OP Stack, starting after the Bedrock upgrade.
+Prospective upgrades may be listed as proposals, but are not governed through these specifications.
+
+Activation rule parameters of network upgrades are configured in respective chain configurations,
+and not part of this specification.
+
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents**
+
+- [Activation rules](#activation-rules)
+  - [L2 Block-number based activation](#l2-block-number-based-activation)
+  - [L2 Block-timestamp based activation](#l2-block-timestamp-based-activation)
+- [Post-Bedrock Network upgrades](#post-bedrock-network-upgrades)
+  - [Regolith](#regolith)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Activation rules
+
+The below L2-block based activation rules may be applied in two contexts:
+
+- The rollup node, specified through the rollup configuration (known as `rollup.json`),
+  referencing L2 blocks (or block input-attributes) that pass through the derivation pipeline.
+- The execution engine, specified through the chain configuration (known as the `config` part of `genesis.json`),
+  referencing blocks or input-attributes that are part of, or applied to, the L2 chain.
+
+### L2 Block-number based activation
+
+Activation rule: `x != null && x >= upgradeNumber`
+
+Starting at, and including, the L2 `block` with `block.number == x`, the upgrade rules apply.
+If the upgrade block-number `x` is not specified in the configuration, the upgrade is ignored.
+
+This applies to the L2 block number, not to the L1-origin block number.
+This means that an L2 upgrade may be inactive, and then active, without changing the L1-origin.
+
+This block number based method has commonly been used in L1 up until the Bellatrix/Paris upgrade, a.k.a. The Merge,
+which was upgraded through special rules.
+
+### L2 Block-timestamp based activation
+
+Activation rule: `x != null && x >= upgradeTime`
+
+Starting at, and including, the L2 `block` with `block.timestamp == x`, the upgrade rules apply.
+If the upgrade block-timestamp `x` is not specified in the configuration, the upgrade is ignored.
+
+This applies to the L2 block timestamp, not to the L1-origin block timestamp.
+This means that an L2 upgrade may be inactive, and then active, without changing the L1-origin.
+
+This timestamp based method has become the default on L1 after the Bellatrix/Paris upgrade, a.k.a. The Merge,
+because it can be planned in accordance with beacon-chain epochs and slots.
+
+Note that the L2 version is not limited to timestamps that match L1 beacon-chain slots or epochs.
+A timestamp may be chosen to be synchronous with a specific slot or epoch on L1,
+but the matching L1-origin information may not be present at the time of activation on L2.
+
+## Post-Bedrock Network upgrades
+
+### Regolith
+
+The Regolith upgrade, named after a material best described as "deposited dust on top of a layer of bedrock",
+implements minor changes to deposit processing, based on reports of the Sherlock Audit-contest and findings in
+the Bedrock Optimism Goerli testnet.
+
+Summary of changes:
+
+- The `isSystemTx` boolean is disabled, system transactions now use the same gas accounting rules as regular deposits.
+- The actual deposit gas-usage is recorded in the receipt of the deposit transaction,
+  and subtracted from the L2 block gas-pool.
+  Unused gas of deposits is not refunded with ETH however, as it is burned on L1.
+- The `nonce` value of the deposit sender account, before the transaction state-transition, is recorded in a new
+  optional field (`depositNonce`), extending the transaction receipt (i.e. not present in pre-Regolith receipts).
+- The recorded deposit `nonce` is used to correct the transaction and receipt metadata in RPC responses,
+  including the `contractAddress` field of deposits that deploy contracts.
+- The `gas` and `depositNonce` data is committed to as part of the consensus-representation of the receipt,
+  enabling the data to be safely synced between independent L2 nodes.
+- The L1-cost function was corrected to more closely match pre-Bedrock behavior.
+
+The [deposit specification](./deposits.md) specifies the deposit changes of the Regolith upgrade in more detail.
+The [execution engine specification](./exec-engine.md) specifies the L1 cost function difference.
+
+The Regolith upgrade uses a *L2 block-timestamp* activation-rule, and is specified in both the
+rollup-node (`regolith_time`) and execution engine (`config.regolithTime`).
diff --git a/specs/rollup-node.md b/specs/rollup-node.md
index 9c79bd2d9..53cbbbfaa 100644
--- a/specs/rollup-node.md
+++ b/specs/rollup-node.md
@@ -24,7 +24,6 @@ currently only concerned with the specification of the rollup driver.
   - [Derivation](#derivation)
 - [L2 Output RPC method](#l2-output-rpc-method)
   - [Output Method API](#output-method-api)
-- [Protocol Version tracking](#protocol-version-tracking)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
@@ -73,20 +72,3 @@ The input and return types here are as defined by the [engine API specs][engine-
 - returns:
   1. `version`: `DATA`, 32 Bytes - the output root version number, beginning with 0.
   1. `l2OutputRoot`: `DATA`, 32 Bytes - the output root.
-
-## Protocol Version tracking
-
-The rollup-node should monitor the recommended and required protocol version by monitoring
-the Protocol Version contract on L1, as specified in the [Superchain Version Signaling specifications].
-
-[Superchain Version Signaling specifications]: ./superchain-upgrades.md#superchain-version-signaling
-
-This can be implemented through polling in the [Driver](#driver) loop.
-After polling the Protocol Version, the rollup node SHOULD communicate it with the execution-engine through an
-[`engine_signalSuperchainV1`](./exec-engine.md#enginesignalsuperchainv1) call.
-
-The rollup node SHOULD warn the user when the recommended version is newer than
-the current version supported by the rollup node.
-
-The rollup node SHOULD take safety precautions if it does not meet the required protocol version.
-This may include halting the engine, with consent of the rollup node operator.
diff --git a/specs/superchain-upgrades.md b/specs/superchain-upgrades.md
deleted file mode 100644
index 244c31862..000000000
--- a/specs/superchain-upgrades.md
+++ /dev/null
@@ -1,274 +0,0 @@
-# Superchain Upgrades
-
-Superchain upgrades, also known as forks or hardforks, implement consensus-breaking changes.
-
-A Superchain upgrade requires the node software to support up to a given Protocol Version.
-The version indicates support, the upgrade indicates the activation of new functionality.
-
-This document lists the protocol versions of the OP-Stack, starting at the Bedrock upgrade,
-as well as the default Superchain Targets.
-
-Activation rule parameters of network upgrades are configured as part of the Superchain Target specification:
-chains following the same Superchain Target upgrade synchronously.
-
-<!-- START doctoc generated TOC please keep comment here to allow auto update -->
-<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
-**Table of Contents**
-
-- [Protocol Version](#protocol-version)
-  - [Protocol Version Format](#protocol-version-format)
-    - [Build identifier](#build-identifier)
-    - [Major versions](#major-versions)
-    - [Minor versions](#minor-versions)
-    - [Patch versions](#patch-versions)
-    - [Pre-releases](#pre-releases)
-  - [Protocol Version Exposure](#protocol-version-exposure)
-- [Superchain Target](#superchain-target)
-  - [Superchain Version signaling](#superchain-version-signaling)
-  - [`ProtocolVersions` L1 contract](#protocolversions-l1-contract)
-- [Activation rules](#activation-rules)
-  - [L2 Block-number based activation (deprecated)](#l2-block-number-based-activation-deprecated)
-  - [L2 Block-timestamp based activation](#l2-block-timestamp-based-activation)
-- [OP-Stack Protocol versions](#op-stack-protocol-versions)
-- [Post-Bedrock Network upgrades](#post-bedrock-network-upgrades)
-  - [Regolith](#regolith)
-
-<!-- END doctoc generated TOC please keep comment here to allow auto update -->
-
-## Protocol Version
-
-The Protocol Version documents the progression of the total set of canonical OP-Stack specifications.
-Components of the OP-Stack implement the subset of their respective protocol component domain,
-up to a given Protocol Version of the OP-Stack.
-
-OP-Stack mods, i.e. non-canonical extensions to the OP-Stack, are not included in the versioning of the Protocol.
-Instead, mods must specify which upstream Protocol Version they are based on and where breaking changes are made.
-This ensures tooling of the OP-Stack can be shared and collaborated on with OP-Stack mods.
-
-The Protocol Version is NOT a hardfork identifier, but rather indicates software-support for a well-defined set
-of features introduced in past and future hardforks, not the activation of said hardforks.
-
-Changes that can be included in prospective Protocol Versions may be included in the specifications as proposals,
-with explicit notice of the Protocol Version they are based on.
-This enables an iterative integration process into the canonical set of specifications,
-but does not guarantee the proposed specifications become canonical.
-
-Note that the Protocol Version only applies to the Protocol specifications with the Superchain Targets specified within.
-This versioning is independent of the [Semver] versioning used in OP Stack smart-contracts,
-and the [Semver]-versioned reference software of the OP-Stack.
-
-### Protocol Version Format
-
-The Protocol Version is [Semver]-compatible.
-It is encoded as a single 32 bytes long `<protocol version>`.
-The version must be encoded as 32 bytes of `DATA` in JSON RPC usage.
-
-The encoding is typed, to ensure future-compatibility.
-
-```text
-<protocol version> ::= <version-type><typed-payload>
-<version-type> ::= <uint8>
-<typed-payload> ::= <31 bytes>
-```
-
-version-type `0`:
-
-```text
-<reserved><build><major><minor><patch><pre-release>
-<reserved> ::= <7 zeroed bytes>
-<build> ::= <8 bytes>
-<major> ::= <big-endian uint32>
-<minor> ::= <big-endian uint32>
-<patch> ::= <big-endian uint32>
-<pre-release> ::= <big-endian uint32>
-```
-
-The `<reserved>` bytes of the Protocol Version are reserved for future extensions.
-
-Protocol versions with a different `<version-type>` should not be compared directly.
-
-[Semver]: https://semver.org/
-
-#### Build identifier
-
-The `<build>` identifier, as defined by [Semver], is ignored when determining version precedence.
-The `<build>` must be non-zero to apply to the protocol version.
-
-Modifications of the OP-Stack should define a `<build>` to distinguish from the canonical protocol feature-set.
-Changes to the `<build>` may be encoded in the `<build>` itself to stay aligned with the upstream protocol.
-The major/minor/patch versions should align with that of the upstream protocol that the modifications are based on.
-Users of the protocol can choose to implement custom support for the alternative `<build>`,
-but may work out of the box if the major features are consistent with that of the upstream protocol version.
-
-The 8 byte `<build>` identifier may be presented as string for human readability if the contents are alpha-numeric,
-including `-` and `.`, as outlined in the [Semver] format specs. Trailing `0` bytes can be used for padding.
-It may be presented as `0x`-prefixed hex string otherwise.
-
-#### Major versions
-
-Major version changes indicate support for new consensus-breaking functionality.
-Major versions should retain support for functionality of previous major versions for
-syncing/indexing of historical chain data.
-Implementations may drop support for previous Major versions, when there are viable alternatives,
-e.g. `l2geth` for pre-Bedrock data.
-
-#### Minor versions
-
-Minor version changes indicate support for backward compatible extensions,
-including backward-compatible additions to the set of chains in a Superchain Target.
-Backward-compatibility is defined by the requirement for existing end-users to upgrade nodes and tools or not.
-Minor version changes may also include optional offchain functionality, such as additional syncing protocols.
-
-#### Patch versions
-
-Patch version changes indicate backward compatible bug fixes and improvements.
-
-#### Pre-releases
-
-Pre-releases of the protocol are proposals: these are not stable targets for production usage.
-A pre-release might not satisfy the intended compatibility requirements as denoted by its associated normal version.
-The `<pre-release>` must be non-zero to apply to the protocol version.
-
-Node-software may support a pre-release, but must not activate any protocol changes without the user explicitly
-opting in through the means of a feature-flag or configuration change.
-
-A pre-release is not an official version, and meant for protocol developers to communicate an experimental changeset
-before the changeset is reviewed by governance. Pre-releases are subject to change.
-
-### Protocol Version Exposure
-
-The Protocol Version is not exposed to the application-layer environment:
-hardforks already expose the change of functionality upon activation as required,
-and the Protocol Version is meant for offchain usage only.
-The protocol version indicates support rather than activation of functionality.
-There is one exception however: signaling by onchain components to offchain components.
-More about this in [Superchain Version signaling].
-
-## Superchain Target
-
-Changes to the L2 state-transition function are transitioned into deterministically across all nodes
-through an **activation rule**.
-
-Changes to L1 smart-contracts must be compatible with the latest activated L2 functionality,
-and are executed through **L1 contract-upgrades**.
-
-A Superchain Target defines a set of activation rules and L1 contract upgrades shared between OP-Stack chains,
-to upgrade the chains collectively.
-
-### Superchain Version signaling
-
-Each Superchain Target tracks the protocol changes, and signals the `recommended` and `required`
-Protocol Version ahead of activation of new breaking functionality.
-
-- `recommended`: a signal in advance of a network upgrade, to alert users of the protocol change to be prepared for.
-  Node software is recommended to signal the recommendation to users through logging and metrics.
-- `required`: a signal shortly in advance of a breaking network upgrade, to alert users of breaking changes.
-  Users may opt in to elevated alerts or preventive measures, to ensure consistency with the upgrade.
-
-Signaling is done through a L1 smart-contract that is monitored by the OP-Stack software.
-Not all components of the OP-Stack are required to directly monitor L1 however:
-cross-component APIs like the Engine API may be used to forward the Protocol Version signals,
-to keep components encapsulated from L1.
-See [`engine_signalOPStackVersionV1`](./exec-engine.md#enginesignalopstackversionv1).
-
-### `ProtocolVersions` L1 contract
-
-The `ProtocolVersions` contract on L1 enables L2 nodes to pick up on superchain protocol version signals.
-
-The interface is:
-
-- Required storage slot: `bytes32(uint256(keccak256("protocolversion.required")) - 1)`
-- Recommended storage slot: `bytes32(uint256(keccak256("protocolversion.recommended")) - 1)`
-- Required getter: `required()` returns `ProtocolVersion`
-- Recommended getter `recommended()` returns `ProtocolVersion`
-- Version updates also emit a typed event:
-  `event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data)`
-
-## Activation rules
-
-The below L2-block based activation rules may be applied in two contexts:
-
-- The rollup node, specified through the rollup configuration (known as `rollup.json`),
-  referencing L2 blocks (or block input-attributes) that pass through the derivation pipeline.
-- The execution engine, specified through the chain configuration (known as the `config` part of `genesis.json`),
-  referencing blocks or input-attributes that are part of, or applied to, the L2 chain.
-
-For both types of configurations, some activation parameters may apply to all chains within the superchain,
-and are then retrieved from the superchain target configuration.
-
-### L2 Block-number based activation (deprecated)
-
-Activation rule: `x != null && x >= upgradeNumber`
-
-This block number based method has commonly been used in L1 up until the Bellatrix/Paris upgrade, a.k.a. The Merge,
-which was upgraded through special rules.
-
-This method is not superchain-compatible, as the activation-parameter is chain-specific
-(different chains may have different block-heights at the same moment in time).
-
-Starting at, and including, the L2 `block` with `block.number == x`, the upgrade rules apply.
-If the upgrade block-number `x` is not specified in the configuration, the upgrade is ignored.
-
-This applies to the L2 block number, not to the L1-origin block number.
-This means that an L2 upgrade may be inactive, and then active, without changing the L1-origin.
-
-### L2 Block-timestamp based activation
-
-Activation rule: `x != null && x >= upgradeTime`
-
-This is the preferred superchain upgrade activation-parameter type:
-it is synchronous between all L2 chains and compatible with post-Merge timestamp-based chain upgrades in L1.
-
-Starting at, and including, the L2 `block` with `block.timestamp == x`, the upgrade rules apply.
-If the upgrade block-timestamp `x` is not specified in the configuration, the upgrade is ignored.
-
-This applies to the L2 block timestamp, not to the L1-origin block timestamp.
-This means that an L2 upgrade may be inactive, and then active, without changing the L1-origin.
-
-This timestamp based method has become the default on L1 after the Bellatrix/Paris upgrade, a.k.a. The Merge,
-because it can be planned in accordance with beacon-chain epochs and slots.
-
-Note that the L2 version is not limited to timestamps that match L1 beacon-chain slots or epochs.
-A timestamp may be chosen to be synchronous with a specific slot or epoch on L1,
-but the matching L1-origin information may not be present at the time of activation on L2.
-
-## OP-Stack Protocol versions
-
-- `v1.0.0`: 2021 Jan 16th - Mainnet Soft Launch, based on OVM.
-  ([announcement](https://medium.com/ethereum-optimism/mainnet-soft-launch-7cacc0143cd5))
-- `v1.1.0`: 2021 Aug 19th - Community launch.
-  ([announcement](https://medium.com/ethereum-optimism/community-launch-7c9a2a9d3e84))
-- `v2.0.0`: 2021 Nov 12th - the EVM-Equivalence update, also known as OVM 2.0 and chain regenesis.
-  ([announcement](https://twitter.com/optimismfnd/status/1458953238867165192))
-- `v2.1.0`: 2022 May 31st - Optimism Collective.
-  ([announcement](https://optimism.mirror.xyz/gQWKlrDqHzdKPsB1iUnI-cVN3v0NvsWnazK7ajlt1fI)).
-- `v3.0.0-1`: 2023 Jan 13th - Bedrock pre-release, deployed on OP-Goerli, and later Base-Goerli.
-- `v3.0.0`: 2023 Jun 6th - Bedrock, including the Regolith hardfork improvements, first deployed on OP-Mainnet.
-
-## Post-Bedrock Network upgrades
-
-### Regolith
-
-The Regolith upgrade, named after a material best described as "deposited dust on top of a layer of bedrock",
-implements minor changes to deposit processing, based on reports of the Sherlock Audit-contest and findings in
-the Bedrock Optimism Goerli testnet.
-
-Summary of changes:
-
-- The `isSystemTx` boolean is disabled, system transactions now use the same gas accounting rules as regular deposits.
-- The actual deposit gas-usage is recorded in the receipt of the deposit transaction,
-  and subtracted from the L2 block gas-pool.
-  Unused gas of deposits is not refunded with ETH however, as it is burned on L1.
-- The `nonce` value of the deposit sender account, before the transaction state-transition, is recorded in a new
-  optional field (`depositNonce`), extending the transaction receipt (i.e. not present in pre-Regolith receipts).
-- The recorded deposit `nonce` is used to correct the transaction and receipt metadata in RPC responses,
-  including the `contractAddress` field of deposits that deploy contracts.
-- The `gas` and `depositNonce` data is committed to as part of the consensus-representation of the receipt,
-  enabling the data to be safely synced between independent L2 nodes.
-- The L1-cost function was corrected to more closely match pre-Bedrock behavior.
-
-The [deposit specification](./deposits.md) specifies the deposit changes of the Regolith upgrade in more detail.
-The [execution engine specification](./exec-engine.md) specifies the L1 cost function difference.
-
-The Regolith upgrade uses a *L2 block-timestamp* activation-rule, and is specified in both the
-rollup-node (`regolith_time`) and execution engine (`config.regolithTime`).


Test patch:
diff --git a/indexer/api/api_test.go b/indexer/api/api_test.go
index 8eb5d7f26..7b99bb8b8 100644
--- a/indexer/api/api_test.go
+++ b/indexer/api/api_test.go
@@ -1,20 +1,17 @@
 package api
 
 import (
-	"encoding/json"
 	"fmt"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 
-	"github.com/ethereum-optimism/optimism/indexer/api/routes"
 	"github.com/ethereum-optimism/optimism/indexer/config"
 	"github.com/ethereum-optimism/optimism/indexer/database"
 	"github.com/ethereum-optimism/optimism/op-node/testlog"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
 )
 
 // MockBridgeTransfersView mocks the BridgeTransfersView interface
@@ -73,8 +70,6 @@ func (mbv *MockBridgeTransfersView) L1BridgeDepositsByAddress(address common.Add
 			{
 				L1BridgeDeposit:   deposit,
 				L1TransactionHash: common.HexToHash("0x123"),
-				L2TransactionHash: common.HexToHash("0x555"),
-				L1BlockHash:       common.HexToHash("0x456"),
 			},
 		},
 	}, nil
@@ -84,11 +79,8 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.
 	return &database.L2BridgeWithdrawalsResponse{
 		Withdrawals: []database.L2BridgeWithdrawalWithTransactionHashes{
 			{
-				L2BridgeWithdrawal:         withdrawal,
-				L2TransactionHash:          common.HexToHash("0x789"),
-				L2BlockHash:                common.HexToHash("0x456"),
-				ProvenL1TransactionHash:    common.HexToHash("0x123"),
-				FinalizedL1TransactionHash: common.HexToHash("0x123"),
+				L2BridgeWithdrawal: withdrawal,
+				L2TransactionHash:  common.HexToHash("0x789"),
 			},
 		},
 	}, nil
@@ -115,17 +107,6 @@ func TestL1BridgeDepositsHandler(t *testing.T) {
 	api.Router.ServeHTTP(responseRecorder, request)
 
 	assert.Equal(t, http.StatusOK, responseRecorder.Code)
-
-	var resp routes.DepositResponse
-	err = json.Unmarshal(responseRecorder.Body.Bytes(), &resp)
-	assert.Nil(t, err)
-
-	require.Len(t, resp.Items, 1)
-
-	assert.Equal(t, resp.Items[0].L1BlockHash, common.HexToHash("0x456").String())
-	assert.Equal(t, resp.Items[0].L1TxHash, common.HexToHash("0x123").String())
-	assert.Equal(t, resp.Items[0].Timestamp, deposit.Tx.Timestamp)
-	assert.Equal(t, resp.Items[0].L2TxHash, common.HexToHash("555").String())
 }
 
 func TestL2BridgeWithdrawalsByAddressHandler(t *testing.T) {
@@ -137,22 +118,5 @@ func TestL2BridgeWithdrawalsByAddressHandler(t *testing.T) {
 	responseRecorder := httptest.NewRecorder()
 	api.Router.ServeHTTP(responseRecorder, request)
 
-	var resp routes.WithdrawalResponse
-	err = json.Unmarshal(responseRecorder.Body.Bytes(), &resp)
-	assert.Nil(t, err)
-
-	require.Len(t, resp.Items, 1)
-
-	assert.Equal(t, resp.Items[0].Guid, withdrawal.TransactionWithdrawalHash.String())
-	assert.Equal(t, resp.Items[0].L2BlockHash, common.HexToHash("0x456").String())
-	assert.Equal(t, resp.Items[0].From, withdrawal.Tx.FromAddress.String())
-	assert.Equal(t, resp.Items[0].To, withdrawal.Tx.ToAddress.String())
-	assert.Equal(t, resp.Items[0].TransactionHash, common.HexToHash("0x789").String())
-	assert.Equal(t, resp.Items[0].Amount, withdrawal.Tx.Amount.String())
-	assert.Equal(t, resp.Items[0].ProofTransactionHash, common.HexToHash("0x123").String())
-	assert.Equal(t, resp.Items[0].ClaimTransactionHash, common.HexToHash("0x123").String())
-	assert.Equal(t, resp.Items[0].L1TokenAddress, withdrawal.TokenPair.RemoteTokenAddress.String())
-	assert.Equal(t, resp.Items[0].L2TokenAddress, withdrawal.TokenPair.LocalTokenAddress.String())
-	assert.Equal(t, resp.Items[0].Timestamp, withdrawal.Tx.Timestamp)
-
+	assert.Equal(t, http.StatusOK, responseRecorder.Code)
 }
diff --git a/indexer/config/config_test.go b/indexer/config/config_test.go
index 07a9716c3..681fd3c8a 100644
--- a/indexer/config/config_test.go
+++ b/indexer/config/config_test.go
@@ -54,10 +54,10 @@ func TestLoadConfig(t *testing.T) {
 	require.NoError(t, err)
 
 	require.Equal(t, conf.Chain.Preset, 420)
-	require.Equal(t, conf.Chain.L1Contracts.OptimismPortalProxy.String(), Presets[420].ChainConfig.L1Contracts.OptimismPortalProxy.String())
-	require.Equal(t, conf.Chain.L1Contracts.L1CrossDomainMessengerProxy.String(), Presets[420].ChainConfig.L1Contracts.L1CrossDomainMessengerProxy.String())
-	require.Equal(t, conf.Chain.L1Contracts.L1StandardBridgeProxy.String(), Presets[420].ChainConfig.L1Contracts.L1StandardBridgeProxy.String())
-	require.Equal(t, conf.Chain.L1Contracts.L2OutputOracleProxy.String(), Presets[420].ChainConfig.L1Contracts.L2OutputOracleProxy.String())
+	require.Equal(t, conf.Chain.L1Contracts.OptimismPortalProxy.String(), presetConfigs[420].L1Contracts.OptimismPortalProxy.String())
+	require.Equal(t, conf.Chain.L1Contracts.L1CrossDomainMessengerProxy.String(), presetConfigs[420].L1Contracts.L1CrossDomainMessengerProxy.String())
+	require.Equal(t, conf.Chain.L1Contracts.L1StandardBridgeProxy.String(), presetConfigs[420].L1Contracts.L1StandardBridgeProxy.String())
+	require.Equal(t, conf.Chain.L1Contracts.L2OutputOracleProxy.String(), presetConfigs[420].L1Contracts.L2OutputOracleProxy.String())
 	require.Equal(t, conf.RPCs.L1RPC, "https://l1.example.com")
 	require.Equal(t, conf.RPCs.L2RPC, "https://l2.example.com")
 	require.Equal(t, conf.DB.Host, "127.0.0.1")
@@ -71,7 +71,7 @@ func TestLoadConfig(t *testing.T) {
 	require.Equal(t, conf.MetricsServer.Port, 7300)
 }
 
-func TestLoadConfigWithoutPreset(t *testing.T) {
+func TestLoadConfig_WithoutPreset(t *testing.T) {
 	tmpfile, err := os.CreateTemp("", "test_without_preset.toml")
 	require.NoError(t, err)
 	defer os.Remove(tmpfile.Name())
@@ -103,6 +103,7 @@ func TestLoadConfigWithoutPreset(t *testing.T) {
 	conf, err := LoadConfig(logger, tmpfile.Name())
 	require.NoError(t, err)
 
+	// Enforce default values
 	require.Equal(t, conf.Chain.L1Contracts.OptimismPortalProxy.String(), common.HexToAddress("0x4205Fc579115071764c7423A4f12eDde41f106Ed").String())
 	require.Equal(t, conf.Chain.L1Contracts.L2OutputOracleProxy.String(), common.HexToAddress("0x42097868233d1aa22e815a266982f2cf17685a27").String())
 	require.Equal(t, conf.Chain.L1Contracts.L1CrossDomainMessengerProxy.String(), common.HexToAddress("0x420ce71c97B33Cc4729CF772ae268934F7ab5fA1").String())
@@ -116,7 +117,7 @@ func TestLoadConfigWithoutPreset(t *testing.T) {
 	require.Equal(t, conf.Chain.L2HeaderBufferSize, uint(500))
 }
 
-func TestLoadConfigWithUnknownPreset(t *testing.T) {
+func TestLoadConfig_WithUnknownPreset(t *testing.T) {
 	tmpfile, err := os.CreateTemp("", "test_bad_preset.toml")
 	require.NoError(t, err)
 	defer os.Remove(tmpfile.Name())
@@ -147,7 +148,7 @@ func TestLoadConfigWithUnknownPreset(t *testing.T) {
 	require.Equal(t, fmt.Sprintf("unknown preset: %d", faultyPreset), err.Error())
 }
 
-func TestLoadConfigPollingValues(t *testing.T) {
+func Test_LoadConfig_PollingValues(t *testing.T) {
 	tmpfile, err := os.CreateTemp("", "test_user_values.toml")
 	require.NoError(t, err)
 	defer os.Remove(tmpfile.Name())
@@ -177,3 +178,25 @@ func TestLoadConfigPollingValues(t *testing.T) {
 	require.Equal(t, conf.Chain.L1HeaderBufferSize, uint(100))
 	require.Equal(t, conf.Chain.L2HeaderBufferSize, uint(105))
 }
+
+func Test_AsSliceSuccess(t *testing.T) {
+	// error cases are intentionally ignored for testing since they can only be
+	// generated when the L1Contracts struct is developer modified to hold a non-address var field
+
+	testCfg := &L1Contracts{
+		OptimismPortalProxy:         common.HexToAddress("0x4205Fc579115071764c7423A4f12eDde41f106Ed"),
+		L2OutputOracleProxy:         common.HexToAddress("0x42097868233d1aa22e815a266982f2cf17685a27"),
+		L1CrossDomainMessengerProxy: common.HexToAddress("0x420ce71c97B33Cc4729CF772ae268934F7ab5fA1"),
+		L1StandardBridgeProxy:       common.HexToAddress("0x4209fc46f92E8a1c0deC1b1747d010903E884bE1"),
+	}
+
+	slice, err := testCfg.AsSlice()
+	require.NoError(t, err)
+	require.Equal(t, len(slice), 5)
+	require.Equal(t, slice[0].String(), testCfg.OptimismPortalProxy.String())
+	require.Equal(t, slice[1].String(), testCfg.L2OutputOracleProxy.String())
+	require.Equal(t, slice[2].String(), testCfg.L1CrossDomainMessengerProxy.String())
+	require.Equal(t, slice[3].String(), testCfg.L1StandardBridgeProxy.String())
+
+	// LegacyCanonicalTransactionChain is the 4th slot
+}
diff --git a/indexer/e2e_tests/bridge_transfers_e2e_test.go b/indexer/e2e_tests/bridge_transfers_e2e_test.go
index 8bebbc2d1..44ed2c120 100644
--- a/indexer/e2e_tests/bridge_transfers_e2e_test.go
+++ b/indexer/e2e_tests/bridge_transfers_e2e_test.go
@@ -57,7 +57,6 @@ func TestE2EBridgeTransfersStandardBridgeETHDeposit(t *testing.T) {
 	require.NoError(t, err)
 	require.Len(t, aliceDeposits.Deposits, 1)
 	require.Equal(t, depositTx.Hash(), aliceDeposits.Deposits[0].L1TransactionHash)
-	require.Equal(t, depositReceipt.BlockHash, aliceDeposits.Deposits[0].L1BlockHash)
 	require.Equal(t, "", aliceDeposits.Cursor)
 	require.Equal(t, false, aliceDeposits.HasNextPage)
 	require.Equal(t, types.NewTx(depositInfo.DepositTx).Hash().String(), aliceDeposits.Deposits[0].L2TransactionHash.String())
@@ -294,7 +293,6 @@ func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, proveReceipt.TxHash, aliceWithdrawals.Withdrawals[0].ProvenL1TransactionHash)
 	require.Equal(t, finalizeReceipt.TxHash, aliceWithdrawals.Withdrawals[0].FinalizedL1TransactionHash)
-	require.Equal(t, withdrawReceipt.BlockHash, aliceWithdrawals.Withdrawals[0].L2BlockHash)
 
 	crossDomainBridgeMessage, err = testSuite.DB.BridgeMessages.L2BridgeMessage(*withdrawal.CrossDomainMessageHash)
 	require.NoError(t, err)
diff --git a/indexer/e2e_tests/etl_e2e_test.go b/indexer/e2e_tests/etl_e2e_test.go
index 8d85ae9e7..d15258303 100644
--- a/indexer/e2e_tests/etl_e2e_test.go
+++ b/indexer/e2e_tests/etl_e2e_test.go
@@ -71,6 +71,45 @@ func TestE2EETL(t *testing.T) {
 		}
 	})
 
+	/*
+		TODO: ADD THIS BACK IN WHEN THESE MARKERS ARE INDEXED
+			t.Run("indexes L2 checkpoints", func(t *testing.T) {
+				latestOutput, err := testSuite.DB.Blocks.LatestCheckpointedOutput()
+				require.NoError(t, err)
+				require.NotNil(t, latestOutput)
+				require.GreaterOrEqual(t, latestOutput.L2BlockNumber.Int.Uint64(), uint64(9))
+
+				l2EthClient, err := node.DialEthClient(testSuite.OpSys.EthInstances["sequencer"].HTTPEndpoint())
+				require.NoError(t, err)
+
+				submissionInterval := testSuite.OpCfg.DeployConfig.L2OutputOracleSubmissionInterval
+				numOutputs := latestOutput.L2BlockNumber.Int.Uint64() / submissionInterval
+				for i := int64(0); i < int64(numOutputs); i++ {
+					blockNumber := big.NewInt((i + 1) * int64(submissionInterval))
+
+					output, err := testSuite.DB.Blocks.OutputProposal(big.NewInt(i))
+					require.NoError(t, err)
+					require.NotNil(t, output)
+					require.Equal(t, i, output.L2OutputIndex.Int.Int64())
+					require.Equal(t, blockNumber, output.L2BlockNumber.Int)
+					require.NotEmpty(t, output.L1ContractEventGUID)
+
+					// we may as well check the integrity of the output root
+					l2Block, err := testSuite.L2Client.BlockByNumber(context.Background(), blockNumber)
+					require.NoError(t, err)
+					messagePasserStorageHash, err := l2EthClient.StorageHash(predeploys.L2ToL1MessagePasserAddr, blockNumber)
+					require.NoError(t, err)
+
+					// construct and check output root
+					outputRootPreImage := [128]byte{}                                 // 4 words (first 32 are zero for version 0)
+					copy(outputRootPreImage[32:64], l2Block.Root().Bytes())           // state root
+					copy(outputRootPreImage[64:96], messagePasserStorageHash.Bytes()) // message passer storage root
+					copy(outputRootPreImage[96:128], l2Block.Hash().Bytes())          // block hash
+					require.Equal(t, crypto.Keccak256Hash(outputRootPreImage[:]), output.OutputRoot)
+				}
+			})
+	*/
+
 	t.Run("indexes L1 blocks with accompanying contract event", func(t *testing.T) {
 		l1Contracts := []common.Address{}
 		testSuite.OpCfg.L1Deployments.ForEach(func(name string, addr common.Address) { l1Contracts = append(l1Contracts, addr) })
diff --git a/indexer/e2e_tests/setup.go b/indexer/e2e_tests/setup.go
index c282a94d5..20e660ee5 100644
--- a/indexer/e2e_tests/setup.go
+++ b/indexer/e2e_tests/setup.go
@@ -71,17 +71,15 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
 			L2RPC: opSys.EthInstances["sequencer"].HTTPEndpoint(),
 		},
 		Chain: config.ChainConfig{
-			L1PollingInterval: uint(opCfg.DeployConfig.L1BlockTime) * 1000,
-			L2PollingInterval: uint(opCfg.DeployConfig.L2BlockTime) * 1000,
-			L2Contracts:       config.L2ContractsFromPredeploys(),
+			L1PollingInterval:   uint(opCfg.DeployConfig.L1BlockTime) * 1000,
+			L1ConfirmationDepth: 0,
+			L2PollingInterval:   uint(opCfg.DeployConfig.L2BlockTime) * 1000,
+			L2ConfirmationDepth: 0,
 			L1Contracts: config.L1Contracts{
-				AddressManager:              opCfg.L1Deployments.AddressManager,
-				SystemConfigProxy:           opCfg.L1Deployments.SystemConfigProxy,
 				OptimismPortalProxy:         opCfg.L1Deployments.OptimismPortalProxy,
 				L2OutputOracleProxy:         opCfg.L1Deployments.L2OutputOracleProxy,
 				L1CrossDomainMessengerProxy: opCfg.L1Deployments.L1CrossDomainMessengerProxy,
 				L1StandardBridgeProxy:       opCfg.L1Deployments.L1StandardBridgeProxy,
-				L1ERC721BridgeProxy:         opCfg.L1Deployments.L1ERC721BridgeProxy,
 			},
 		},
 		HTTPServer:    config.ServerConfig{Host: "127.0.0.1", Port: 0},
diff --git a/indexer/etl/l1_etl_test.go b/indexer/etl/l1_etl_test.go
index b9673162b..343d48bbc 100644
--- a/indexer/etl/l1_etl_test.go
+++ b/indexer/etl/l1_etl_test.go
@@ -51,12 +51,10 @@ func TestL1ETLConstruction(t *testing.T) {
 				client.On("GethEthClient").Return(nil)
 
 				return &testSuite{
-					db:     db,
-					client: client,
-					start:  testStart,
-
-					// utilize sample l1 contract configuration (optimism)
-					contracts: config.Presets[10].ChainConfig.L1Contracts,
+					db:        db,
+					client:    client,
+					start:     testStart,
+					contracts: config.L1Contracts{},
 				}
 			},
 			assertion: func(etl *L1ETL, err error) {
@@ -83,12 +81,10 @@ func TestL1ETLConstruction(t *testing.T) {
 				client.On("GethEthClient").Return(nil)
 
 				return &testSuite{
-					db:     db,
-					client: client,
-					start:  testStart,
-
-					// utilize sample l1 contract configuration (optimism)
-					contracts: config.Presets[10].ChainConfig.L1Contracts,
+					db:        db,
+					client:    client,
+					start:     testStart,
+					contracts: config.L1Contracts{},
 				}
 			},
 			assertion: func(etl *L1ETL, err error) {
diff --git a/op-challenger/cmd/main_test.go b/op-challenger/cmd/main_test.go
index f20142750..ae32ec9e4 100644
--- a/op-challenger/cmd/main_test.go
+++ b/op-challenger/cmd/main_test.go
@@ -169,26 +169,6 @@ func TestMaxConcurrency(t *testing.T) {
 	})
 }
 
-func TestPollInterval(t *testing.T) {
-	t.Run("UsesDefault", func(t *testing.T) {
-		cfg := configForArgs(t, addRequiredArgs(config.TraceTypeCannon))
-		require.Equal(t, config.DefaultPollInterval, cfg.PollInterval)
-	})
-
-	t.Run("Valid", func(t *testing.T) {
-		expected := 100 * time.Second
-		cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet, "--http-poll-interval", "100s"))
-		require.Equal(t, expected, cfg.PollInterval)
-	})
-
-	t.Run("Invalid", func(t *testing.T) {
-		verifyArgsInvalid(
-			t,
-			"invalid value \"abc\" for flag -http-poll-interval",
-			addRequiredArgs(config.TraceTypeAlphabet, "--http-poll-interval", "abc"))
-	})
-}
-
 func TestCannonBin(t *testing.T) {
 	t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
 		configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-bin"))
diff --git a/op-challenger/config/config_test.go b/op-challenger/config/config_test.go
index 6696eeff8..0c3fcaffa 100644
--- a/op-challenger/config/config_test.go
+++ b/op-challenger/config/config_test.go
@@ -118,13 +118,6 @@ func TestMaxConcurrency(t *testing.T) {
 	})
 }
 
-func TestHttpPollInterval(t *testing.T) {
-	t.Run("Default", func(t *testing.T) {
-		config := validConfig(TraceTypeAlphabet)
-		require.EqualValues(t, DefaultPollInterval, config.PollInterval)
-	})
-}
-
 func TestCannonL2Required(t *testing.T) {
 	config := validConfig(TraceTypeCannon)
 	config.CannonL2 = ""
diff --git a/op-challenger/game/fault/solver/game_solver_test.go b/op-challenger/game/fault/solver/game_solver_test.go
index 810d2c149..396e84ea8 100644
--- a/op-challenger/game/fault/solver/game_solver_test.go
+++ b/op-challenger/game/fault/solver/game_solver_test.go
@@ -84,27 +84,6 @@ func TestCalculateNextActions(t *testing.T) {
 				lastHonestClaim.Attack(common.Hash{0xdd}).ExpectStepAttack()
 			},
 		},
-
-		{
-			name:                "PoisonedPreState",
-			agreeWithOutputRoot: true,
-			setupGame: func(builder *faulttest.GameBuilder) {
-				// A claim hash that has no pre-image
-				maliciousStateHash := common.Hash{0x01, 0xaa}
-
-				// Dishonest actor counters their own claims to set up a situation with an invalid prestate
-				// The honest actor should attack all claims that support the root claim (disagree with the output root)
-				builder.Seq().ExpectAttack(). // This expected action is the winning move.
-								Attack(maliciousStateHash).
-								Defend(maliciousStateHash).ExpectAttack().
-								Attack(maliciousStateHash).
-								Attack(maliciousStateHash).ExpectStepAttack()
-
-				// The attempt to step against our malicious leaf node will fail because the pre-state won't match our
-				// malicious state hash. However, it is the very first expected action, attacking the root claim with
-				// the correct hash that wins the game since it will be the left-most uncountered claim.
-			},
-		},
 	}
 
 	for _, test := range tests {
@@ -114,8 +93,7 @@ func TestCalculateNextActions(t *testing.T) {
 			test.setupGame(builder)
 			game := builder.Game
 			for i, claim := range game.Claims() {
-				t.Logf("Claim %v: Pos: %v TraceIdx: %v ParentIdx: %v, Countered: %v, Value: %v",
-					i, claim.Position.ToGIndex(), claim.Position.TraceIndex(maxDepth), claim.ParentContractIndex, claim.Countered, claim.Value)
+				t.Logf("Claim %v: Pos: %v ParentIdx: %v, Countered: %v, Value: %v", i, claim.Position.ToGIndex(), claim.ParentContractIndex, claim.Countered, claim.Value)
 			}
 
 			solver := NewGameSolver(maxDepth, claimBuilder.CorrectTraceProvider())
diff --git a/op-challenger/game/fault/trace/cannon/executor_test.go b/op-challenger/game/fault/trace/cannon/executor_test.go
index f574c47ee..752b9be69 100644
--- a/op-challenger/game/fault/trace/cannon/executor_test.go
+++ b/op-challenger/game/fault/trace/cannon/executor_test.go
@@ -40,8 +40,7 @@ func TestGenerateProof(t *testing.T) {
 		L2BlockNumber: big.NewInt(3333),
 	}
 	captureExec := func(t *testing.T, cfg config.Config, proofAt uint64) (string, string, map[string]string) {
-		m := &cannonDurationMetrics{}
-		executor := NewExecutor(testlog.Logger(t, log.LvlInfo), m, &cfg, inputs)
+		executor := NewExecutor(testlog.Logger(t, log.LvlInfo), metrics.NoopMetrics, &cfg, inputs)
 		executor.selectSnapshot = func(logger log.Logger, dir string, absolutePreState string, i uint64) (string, error) {
 			return input, nil
 		}
@@ -64,7 +63,6 @@ func TestGenerateProof(t *testing.T) {
 		}
 		err := executor.GenerateProof(context.Background(), dir, proofAt)
 		require.NoError(t, err)
-		require.Equal(t, 1, m.executionTimeRecordCount, "Should record cannon execution time")
 		return binary, subcommand, args
 	}
 
@@ -213,12 +211,3 @@ func TestFindStartingSnapshot(t *testing.T) {
 		require.Equal(t, filepath.Join(dir, "100.json.gz"), snapshot)
 	})
 }
-
-type cannonDurationMetrics struct {
-	metrics.NoopMetricsImpl
-	executionTimeRecordCount int
-}
-
-func (c *cannonDurationMetrics) RecordCannonExecutionTime(_ float64) {
-	c.executionTimeRecordCount++
-}
diff --git a/op-challenger/game/fault/trace/cannon/provider_test.go b/op-challenger/game/fault/trace/cannon/provider_test.go
index 9b8eeb83f..7d6eac134 100644
--- a/op-challenger/game/fault/trace/cannon/provider_test.go
+++ b/op-challenger/game/fault/trace/cannon/provider_test.go
@@ -65,8 +65,8 @@ func TestGet(t *testing.T) {
 }
 
 func TestGetStepData(t *testing.T) {
+	dataDir, prestate := setupTestData(t)
 	t.Run("ExistingProof", func(t *testing.T) {
-		dataDir, prestate := setupTestData(t)
 		provider, generator := setupWithTestData(t, dataDir, prestate)
 		value, proof, data, err := provider.GetStepData(context.Background(), 0)
 		require.NoError(t, err)
@@ -80,7 +80,6 @@ func TestGetStepData(t *testing.T) {
 	})
 
 	t.Run("GenerateProof", func(t *testing.T) {
-		dataDir, prestate := setupTestData(t)
 		provider, generator := setupWithTestData(t, dataDir, prestate)
 		generator.finalState = &mipsevm.State{
 			Memory: &mipsevm.Memory{},
@@ -106,7 +105,6 @@ func TestGetStepData(t *testing.T) {
 	})
 
 	t.Run("ProofAfterEndOfTrace", func(t *testing.T) {
-		dataDir, prestate := setupTestData(t)
 		provider, generator := setupWithTestData(t, dataDir, prestate)
 		generator.finalState = &mipsevm.State{
 			Memory: &mipsevm.Memory{},
@@ -131,48 +129,7 @@ func TestGetStepData(t *testing.T) {
 		require.Nil(t, data)
 	})
 
-	t.Run("ReadLastStepFromDisk", func(t *testing.T) {
-		dataDir, prestate := setupTestData(t)
-		provider, initGenerator := setupWithTestData(t, dataDir, prestate)
-		initGenerator.finalState = &mipsevm.State{
-			Memory: &mipsevm.Memory{},
-			Step:   10,
-			Exited: true,
-		}
-		initGenerator.proof = &proofData{
-			ClaimValue:   common.Hash{0xaa},
-			StateData:    []byte{0xbb},
-			ProofData:    []byte{0xcc},
-			OracleKey:    common.Hash{0xdd}.Bytes(),
-			OracleValue:  []byte{0xdd},
-			OracleOffset: 10,
-		}
-		_, _, _, err := provider.GetStepData(context.Background(), 7000)
-		require.NoError(t, err)
-		require.Contains(t, initGenerator.generated, 7000, "should have tried to generate the proof")
-
-		provider, generator := setupWithTestData(t, dataDir, prestate)
-		generator.finalState = &mipsevm.State{
-			Memory: &mipsevm.Memory{},
-			Step:   10,
-			Exited: true,
-		}
-		generator.proof = &proofData{
-			ClaimValue: common.Hash{0xaa},
-			StateData:  []byte{0xbb},
-			ProofData:  []byte{0xcc},
-		}
-		preimage, proof, data, err := provider.GetStepData(context.Background(), 7000)
-		require.NoError(t, err)
-		require.Empty(t, generator.generated, "should not have to generate the proof again")
-
-		require.EqualValues(t, initGenerator.finalState.EncodeWitness(), preimage)
-		require.Empty(t, proof)
-		require.Nil(t, data)
-	})
-
 	t.Run("MissingStateData", func(t *testing.T) {
-		dataDir, prestate := setupTestData(t)
 		provider, generator := setupWithTestData(t, dataDir, prestate)
 		_, _, _, err := provider.GetStepData(context.Background(), 1)
 		require.ErrorContains(t, err, "missing state data")
@@ -180,7 +137,6 @@ func TestGetStepData(t *testing.T) {
 	})
 
 	t.Run("IgnoreUnknownFields", func(t *testing.T) {
-		dataDir, prestate := setupTestData(t)
 		provider, generator := setupWithTestData(t, dataDir, prestate)
 		value, proof, data, err := provider.GetStepData(context.Background(), 2)
 		require.NoError(t, err)
diff --git a/op-challenger/game/monitor_test.go b/op-challenger/game/monitor_test.go
index 614eb13da..732554c0b 100644
--- a/op-challenger/game/monitor_test.go
+++ b/op-challenger/game/monitor_test.go
@@ -2,40 +2,35 @@ package game
 
 import (
 	"context"
-	"fmt"
 	"math/big"
 	"testing"
 	"time"
 
-	"github.com/ethereum/go-ethereum"
+	"github.com/ethereum-optimism/optimism/op-node/testlog"
+	"github.com/ethereum-optimism/optimism/op-service/clock"
 	"github.com/ethereum/go-ethereum/common"
-	ethtypes "github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/stretchr/testify/require"
-
-	"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
-	"github.com/ethereum-optimism/optimism/op-node/testlog"
-	"github.com/ethereum-optimism/optimism/op-service/clock"
 )
 
 func TestMonitorMinGameTimestamp(t *testing.T) {
 	t.Parallel()
 
 	t.Run("zero game window returns zero", func(t *testing.T) {
-		monitor, _, _, _ := setupMonitorTest(t, []common.Address{})
+		monitor, _, _ := setupMonitorTest(t, []common.Address{})
 		monitor.gameWindow = time.Duration(0)
 		require.Equal(t, monitor.minGameTimestamp(), uint64(0))
 	})
 
 	t.Run("non-zero game window with zero clock", func(t *testing.T) {
-		monitor, _, _, _ := setupMonitorTest(t, []common.Address{})
+		monitor, _, _ := setupMonitorTest(t, []common.Address{})
 		monitor.gameWindow = time.Minute
 		monitor.clock = clock.NewDeterministicClock(time.Unix(0, 0))
 		require.Equal(t, monitor.minGameTimestamp(), uint64(0))
 	})
 
 	t.Run("minimum computed correctly", func(t *testing.T) {
-		monitor, _, _, _ := setupMonitorTest(t, []common.Address{})
+		monitor, _, _ := setupMonitorTest(t, []common.Address{})
 		monitor.gameWindow = time.Minute
 		frozen := time.Unix(int64(time.Hour.Seconds()), 0)
 		monitor.clock = clock.NewDeterministicClock(frozen)
@@ -44,95 +39,29 @@ func TestMonitorMinGameTimestamp(t *testing.T) {
 	})
 }
 
-// TestMonitorGames tests that the monitor can handle a new head event
-// and resubscribe to new heads if the subscription errors.
-func TestMonitorGames(t *testing.T) {
-	t.Run("Schedules games", func(t *testing.T) {
-		addr1 := common.Address{0xaa}
-		addr2 := common.Address{0xbb}
-		monitor, source, sched, mockHeadSource := setupMonitorTest(t, []common.Address{})
-		source.games = []FaultDisputeGame{newFDG(addr1, 9999), newFDG(addr2, 9999)}
-
-		ctx, cancel := context.WithCancel(context.Background())
-		defer cancel()
-
-		go func() {
-			headerNotSent := true
-			waitErr := wait.For(context.Background(), 100*time.Millisecond, func() (bool, error) {
-				if len(sched.scheduled) >= 1 {
-					return true, nil
-				}
-				if mockHeadSource.sub == nil {
-					return false, nil
-				}
-				if headerNotSent {
-					mockHeadSource.sub.headers <- &ethtypes.Header{
-						Number: big.NewInt(1),
-					}
-					headerNotSent = false
-				}
-				return false, nil
-			})
-			require.NoError(t, waitErr)
-			mockHeadSource.err = fmt.Errorf("eth subscribe test error")
-			cancel()
-		}()
-
-		err := monitor.MonitorGames(ctx)
-		require.NoError(t, err)
-		require.Len(t, sched.scheduled, 1)
-		require.Equal(t, []common.Address{addr1, addr2}, sched.scheduled[0])
-	})
-
-	t.Run("Resubscribes on error", func(t *testing.T) {
-		addr1 := common.Address{0xaa}
-		addr2 := common.Address{0xbb}
-		monitor, source, sched, mockHeadSource := setupMonitorTest(t, []common.Address{})
-		source.games = []FaultDisputeGame{newFDG(addr1, 9999), newFDG(addr2, 9999)}
-
-		ctx, cancel := context.WithCancel(context.Background())
-		defer cancel()
-
-		go func() {
-			headerNotSent := true
-			waitErr := wait.For(context.Background(), 100*time.Millisecond, func() (bool, error) {
-				return mockHeadSource.sub != nil, nil
-			})
-			require.NoError(t, waitErr)
-			mockHeadSource.sub.errChan <- fmt.Errorf("test error")
-			waitErr = wait.For(context.Background(), 100*time.Millisecond, func() (bool, error) {
-				if len(sched.scheduled) >= 1 {
-					return true, nil
-				}
-				if mockHeadSource.sub == nil {
-					return false, nil
-				}
-				if headerNotSent {
-					mockHeadSource.sub.headers <- &ethtypes.Header{
-						Number: big.NewInt(1),
-					}
-					headerNotSent = false
-				}
-				return false, nil
-			})
-			require.NoError(t, waitErr)
-			mockHeadSource.err = fmt.Errorf("eth subscribe test error")
-			cancel()
-		}()
-
-		err := monitor.MonitorGames(ctx)
-		require.NoError(t, err)
-		require.Len(t, sched.scheduled, 1)
-		require.Equal(t, []common.Address{addr1, addr2}, sched.scheduled[0])
-	})
+func TestMonitorExitsWhenContextDone(t *testing.T) {
+	monitor, _, _ := setupMonitorTest(t, []common.Address{{}})
+	ctx, cancel := context.WithCancel(context.Background())
+	cancel()
+	err := monitor.MonitorGames(ctx)
+	require.ErrorIs(t, err, context.Canceled)
 }
 
 func TestMonitorCreateAndProgressGameAgents(t *testing.T) {
-	monitor, source, sched, _ := setupMonitorTest(t, []common.Address{})
+	monitor, source, sched := setupMonitorTest(t, []common.Address{})
 
 	addr1 := common.Address{0xaa}
 	addr2 := common.Address{0xbb}
-	source.games = []FaultDisputeGame{newFDG(addr1, 9999), newFDG(addr2, 9999)}
+	source.games = []FaultDisputeGame{
+		{
+			Proxy:     addr1,
+			Timestamp: 9999,
+		},
+		{
+			Proxy:     addr2,
+			Timestamp: 9999,
+		},
+	}
 
 	require.NoError(t, monitor.progressGames(context.Background(), uint64(1)))
 
@@ -143,8 +72,18 @@ func TestMonitorCreateAndProgressGameAgents(t *testing.T) {
 func TestMonitorOnlyScheduleSpecifiedGame(t *testing.T) {
 	addr1 := common.Address{0xaa}
 	addr2 := common.Address{0xbb}
-	monitor, source, sched, _ := setupMonitorTest(t, []common.Address{addr2})
-	source.games = []FaultDisputeGame{newFDG(addr1, 9999), newFDG(addr2, 9999)}
+	monitor, source, sched := setupMonitorTest(t, []common.Address{addr2})
+
+	source.games = []FaultDisputeGame{
+		{
+			Proxy:     addr1,
+			Timestamp: 9999,
+		},
+		{
+			Proxy:     addr2,
+			Timestamp: 9999,
+		},
+	}
 
 	require.NoError(t, monitor.progressGames(context.Background(), uint64(1)))
 
@@ -152,17 +91,7 @@ func TestMonitorOnlyScheduleSpecifiedGame(t *testing.T) {
 	require.Equal(t, []common.Address{addr2}, sched.scheduled[0])
 }
 
-func newFDG(proxy common.Address, timestamp uint64) FaultDisputeGame {
-	return FaultDisputeGame{
-		Proxy:     proxy,
-		Timestamp: timestamp,
-	}
-}
-
-func setupMonitorTest(
-	t *testing.T,
-	allowedGames []common.Address,
-) (*gameMonitor, *stubGameSource, *stubScheduler, *mockNewHeadSource) {
+func setupMonitorTest(t *testing.T, allowedGames []common.Address) (*gameMonitor, *stubGameSource, *stubScheduler) {
 	logger := testlog.Logger(t, log.LvlDebug)
 	source := &stubGameSource{}
 	i := uint64(1)
@@ -171,58 +100,15 @@ func setupMonitorTest(
 		return i, nil
 	}
 	sched := &stubScheduler{}
-	mockHeadSource := &mockNewHeadSource{}
-	monitor := newGameMonitor(
-		logger,
-		clock.SystemClock,
-		source,
-		sched,
-		time.Duration(0),
-		fetchBlockNum,
-		allowedGames,
-		mockHeadSource,
-	)
-	return monitor, source, sched, mockHeadSource
-}
-
-type mockNewHeadSource struct {
-	sub *mockSubscription
-	err error
-}
-
-func (m *mockNewHeadSource) EthSubscribe(
-	ctx context.Context,
-	ch any,
-	args ...any,
-) (ethereum.Subscription, error) {
-	errChan := make(chan error)
-	m.sub = &mockSubscription{errChan, (ch).(chan<- *ethtypes.Header)}
-	if m.err != nil {
-		return nil, m.err
-	}
-	return m.sub, nil
-}
-
-type mockSubscription struct {
-	errChan chan error
-	headers chan<- *ethtypes.Header
-}
-
-func (m *mockSubscription) Unsubscribe() {}
-
-func (m *mockSubscription) Err() <-chan error {
-	return m.errChan
+	monitor := newGameMonitor(logger, clock.SystemClock, source, sched, time.Duration(0), fetchBlockNum, allowedGames)
+	return monitor, source, sched
 }
 
 type stubGameSource struct {
 	games []FaultDisputeGame
 }
 
-func (s *stubGameSource) FetchAllGamesAtBlock(
-	ctx context.Context,
-	earliest uint64,
-	blockNumber *big.Int,
-) ([]FaultDisputeGame, error) {
+func (s *stubGameSource) FetchAllGamesAtBlock(ctx context.Context, earliest uint64, blockNumber *big.Int) ([]FaultDisputeGame, error) {
 	return s.games, nil
 }
 
diff --git a/op-e2e/e2eutils/challenger/helper.go b/op-e2e/e2eutils/challenger/helper.go
index fd90661cb..abca80022 100644
--- a/op-e2e/e2eutils/challenger/helper.go
+++ b/op-e2e/e2eutils/challenger/helper.go
@@ -145,10 +145,6 @@ func NewChallengerConfig(t *testing.T, l1Endpoint string, options ...Option) *co
 		_, err := os.Stat(cfg.CannonAbsolutePreState)
 		require.NoError(t, err, "cannon pre-state should be built. Make sure you've run make cannon-prestate")
 	}
-	if cfg.PollInterval == 0 {
-		cfg.PollInterval = time.Second
-	}
-
 	return &cfg
 }
 
diff --git a/op-e2e/e2eutils/disputegame/game_helper.go b/op-e2e/e2eutils/disputegame/game_helper.go
index 868772b1c..d78cd7032 100644
--- a/op-e2e/e2eutils/disputegame/game_helper.go
+++ b/op-e2e/e2eutils/disputegame/game_helper.go
@@ -65,7 +65,7 @@ func (g *FaultGameHelper) MaxDepth(ctx context.Context) int64 {
 }
 
 func (g *FaultGameHelper) waitForClaim(ctx context.Context, errorMsg string, predicate func(claim ContractClaim) bool) {
-	timedCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
+	timedCtx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	err := wait.For(timedCtx, time.Second, func() (bool, error) {
 		count, err := g.game.ClaimDataLen(&bind.CallOpts{Context: timedCtx})
@@ -89,31 +89,6 @@ func (g *FaultGameHelper) waitForClaim(ctx context.Context, errorMsg string, pre
 	}
 }
 
-func (g *FaultGameHelper) waitForNoClaim(ctx context.Context, errorMsg string, predicate func(claim ContractClaim) bool) {
-	timedCtx, cancel := context.WithTimeout(ctx, 3*time.Minute)
-	defer cancel()
-	err := wait.For(timedCtx, time.Second, func() (bool, error) {
-		count, err := g.game.ClaimDataLen(&bind.CallOpts{Context: timedCtx})
-		if err != nil {
-			return false, fmt.Errorf("retrieve number of claims: %w", err)
-		}
-		// Search backwards because the new claims are at the end and more likely the ones we will fail on.
-		for i := count.Int64() - 1; i >= 0; i-- {
-			claimData, err := g.game.ClaimData(&bind.CallOpts{Context: timedCtx}, big.NewInt(i))
-			if err != nil {
-				return false, fmt.Errorf("retrieve claim %v: %w", i, err)
-			}
-			if predicate(claimData) {
-				return false, nil
-			}
-		}
-		return true, nil
-	})
-	if err != nil { // Avoid waiting time capturing game data when there's no error
-		g.require.NoErrorf(err, "%v\n%v", errorMsg, g.gameData(ctx))
-	}
-}
-
 func (g *FaultGameHelper) GetClaimValue(ctx context.Context, claimIdx int64) common.Hash {
 	g.WaitForClaimCount(ctx, claimIdx+1)
 	claim := g.getClaim(ctx, claimIdx)
@@ -130,16 +105,6 @@ func (g *FaultGameHelper) getClaim(ctx context.Context, claimIdx int64) Contract
 	return claimData
 }
 
-func (g *FaultGameHelper) WaitForClaimAtDepth(ctx context.Context, depth int) {
-	g.waitForClaim(
-		ctx,
-		fmt.Sprintf("Could not find claim depth %v", depth),
-		func(claim ContractClaim) bool {
-			pos := types.NewPositionFromGIndex(claim.Position.Uint64())
-			return pos.Depth() == depth
-		})
-}
-
 func (g *FaultGameHelper) WaitForClaimAtMaxDepth(ctx context.Context, countered bool) {
 	maxDepth := g.MaxDepth(ctx)
 	g.waitForClaim(
@@ -151,15 +116,6 @@ func (g *FaultGameHelper) WaitForClaimAtMaxDepth(ctx context.Context, countered
 		})
 }
 
-func (g *FaultGameHelper) WaitForAllClaimsCountered(ctx context.Context) {
-	g.waitForNoClaim(
-		ctx,
-		"Did not find all claims countered",
-		func(claim ContractClaim) bool {
-			return !claim.Countered
-		})
-}
-
 func (g *FaultGameHelper) Resolve(ctx context.Context) {
 	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
diff --git a/op-e2e/e2eutils/disputegame/helper.go b/op-e2e/e2eutils/disputegame/helper.go
index 0e22c1c6b..495b3cc61 100644
--- a/op-e2e/e2eutils/disputegame/helper.go
+++ b/op-e2e/e2eutils/disputegame/helper.go
@@ -100,7 +100,7 @@ func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet s
 	l2BlockNumber := h.waitForProposals(ctx)
 	l1Head := h.checkpointL1Block(ctx)
 
-	ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
 	defer cancel()
 
 	trace := alphabet.NewTraceProvider(claimedAlphabet, alphabetGameDepth)
diff --git a/op-e2e/faultproof_test.go b/op-e2e/faultproof_test.go
index d3ed7b429..196979a00 100644
--- a/op-e2e/faultproof_test.go
+++ b/op-e2e/faultproof_test.go
@@ -341,66 +341,6 @@ func TestCannonProposedOutputRootInvalid(t *testing.T) {
 	}
 }
 
-func TestCannonPoisonedPostState(t *testing.T) {
-	t.Skip("Known failure case")
-	InitParallel(t)
-
-	ctx := context.Background()
-	sys, l1Client := startFaultDisputeSystem(t)
-	t.Cleanup(sys.Close)
-
-	l1Endpoint := sys.NodeEndpoint("l1")
-	l2Endpoint := sys.NodeEndpoint("sequencer")
-
-	disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
-	game, correctTrace := disputeGameFactory.StartCannonGameWithCorrectRoot(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint,
-		challenger.WithPrivKey(sys.cfg.Secrets.Mallory),
-	)
-	require.NotNil(t, game)
-	game.LogGameData(ctx)
-
-	// Honest first attack at "honest" level
-	correctTrace.Attack(ctx, 0)
-
-	// Honest defense at "dishonest" level
-	correctTrace.Defend(ctx, 1)
-
-	// Dishonest attack at "honest" level - honest move would be to defend
-	game.Attack(ctx, 2, common.Hash{0x03, 0xaa})
-
-	// Start the honest challenger
-	game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, "Honest",
-		// Agree with the proposed output, so disagree with the root claim
-		challenger.WithAgreeProposedOutput(true),
-		challenger.WithPrivKey(sys.cfg.Secrets.Bob),
-	)
-
-	// Start dishonest challenger that posts correct claims
-	game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, "DishonestCorrect",
-		// Disagree with the proposed output, so agree with the root claim
-		challenger.WithAgreeProposedOutput(false),
-		challenger.WithPrivKey(sys.cfg.Secrets.Mallory),
-	)
-
-	// Give the challengers time to progress down the full game depth
-	depth := game.MaxDepth(ctx)
-	for i := 3; i <= int(depth); i++ {
-		game.WaitForClaimAtDepth(ctx, i)
-		game.LogGameData(ctx)
-	}
-
-	// Wait for all the leaf nodes to be countered
-	// Wait for the challengers to drive the game down to the leaf node which should be countered
-	game.WaitForAllClaimsCountered(ctx)
-
-	// Time travel past when the game will be resolvable.
-	sys.TimeTravelClock.AdvanceTime(game.GameDuration(ctx))
-	require.NoError(t, wait.ForNextBlock(ctx, l1Client))
-
-	game.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
-	game.LogGameData(ctx)
-}
-
 // setupDisputeGameForInvalidOutputRoot sets up an L2 chain with at least one valid output root followed by an invalid output root.
 // A cannon dispute game is started to dispute the invalid output root with the correct root claim provided.
 // An honest challenger is run to defend the root claim (ie disagree with the invalid output root).
diff --git a/op-e2e/l2_gossip_test.go b/op-e2e/l2_gossip_test.go
index 530247401..b20107ca5 100644
--- a/op-e2e/l2_gossip_test.go
+++ b/op-e2e/l2_gossip_test.go
@@ -14,8 +14,8 @@ func TestTxGossip(t *testing.T) {
 	gethOpts := []geth.GethOption{
 		geth.WithP2P(),
 	}
-	cfg.GethOptions["sequencer"] = gethOpts
-	cfg.GethOptions["verifier"] = gethOpts
+	cfg.GethOptions["sequencer"] = append(cfg.GethOptions["sequencer"], gethOpts...)
+	cfg.GethOptions["verifier"] = append(cfg.GethOptions["verifier"], gethOpts...)
 	sys, err := cfg.Start(t)
 	require.NoError(t, err, "Start system")
 
diff --git a/op-e2e/setup.go b/op-e2e/setup.go
index e88d12f9a..dcc0237df 100644
--- a/op-e2e/setup.go
+++ b/op-e2e/setup.go
@@ -408,16 +408,17 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
 				L2Time:       uint64(cfg.DeployConfig.L1GenesisBlockTimestamp),
 				SystemConfig: e2eutils.SystemConfigFromDeployConfig(cfg.DeployConfig),
 			},
-			BlockTime:              cfg.DeployConfig.L2BlockTime,
-			MaxSequencerDrift:      cfg.DeployConfig.MaxSequencerDrift,
-			SeqWindowSize:          cfg.DeployConfig.SequencerWindowSize,
-			ChannelTimeout:         cfg.DeployConfig.ChannelTimeout,
-			L1ChainID:              cfg.L1ChainIDBig(),
-			L2ChainID:              cfg.L2ChainIDBig(),
-			BatchInboxAddress:      cfg.DeployConfig.BatchInboxAddress,
-			DepositContractAddress: cfg.DeployConfig.OptimismPortalProxy,
-			L1SystemConfigAddress:  cfg.DeployConfig.SystemConfigProxy,
-			RegolithTime:           cfg.DeployConfig.RegolithTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)),
+			BlockTime:               cfg.DeployConfig.L2BlockTime,
+			MaxSequencerDrift:       cfg.DeployConfig.MaxSequencerDrift,
+			SeqWindowSize:           cfg.DeployConfig.SequencerWindowSize,
+			ChannelTimeout:          cfg.DeployConfig.ChannelTimeout,
+			L1ChainID:               cfg.L1ChainIDBig(),
+			L2ChainID:               cfg.L2ChainIDBig(),
+			BatchInboxAddress:       cfg.DeployConfig.BatchInboxAddress,
+			DepositContractAddress:  cfg.DeployConfig.OptimismPortalProxy,
+			L1SystemConfigAddress:   cfg.DeployConfig.SystemConfigProxy,
+			RegolithTime:            cfg.DeployConfig.RegolithTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)),
+			ProtocolVersionsAddress: cfg.L1Deployments.ProtocolVersionsProxy,
 		}
 	}
 	defaultConfig := makeRollupConfig()
diff --git a/op-e2e/system_test.go b/op-e2e/system_test.go
index b935bc88b..aca2969a8 100644
--- a/op-e2e/system_test.go
+++ b/op-e2e/system_test.go
@@ -2,6 +2,7 @@ package op_e2e
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"math/big"
 	"os"
@@ -261,20 +262,20 @@ func TestPendingGasLimit(t *testing.T) {
 
 	// configure the L2 gas limit to be high, and the pending gas limits to be lower for resource saving.
 	cfg.DeployConfig.L2GenesisBlockGasLimit = 30_000_000
-	cfg.GethOptions["sequencer"] = []geth.GethOption{
+	cfg.GethOptions["sequencer"] = append(cfg.GethOptions["sequencer"], []geth.GethOption{
 		func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error {
 			ethCfg.Miner.GasCeil = 10_000_000
 			ethCfg.Miner.RollupComputePendingBlock = true
 			return nil
 		},
-	}
-	cfg.GethOptions["verifier"] = []geth.GethOption{
+	}...)
+	cfg.GethOptions["verifier"] = append(cfg.GethOptions["verifier"], []geth.GethOption{
 		func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error {
 			ethCfg.Miner.GasCeil = 9_000_000
 			ethCfg.Miner.RollupComputePendingBlock = true
 			return nil
 		},
-	}
+	}...)
 
 	sys, err := cfg.Start(t)
 	require.Nil(t, err, "Error starting up system")
@@ -1434,3 +1435,118 @@ func TestRuntimeConfigReload(t *testing.T) {
 	})
 	require.NoError(t, err)
 }
+
+func TestRecommendedProtocolVersionChange(t *testing.T) {
+	InitParallel(t)
+
+	cfg := DefaultSystemConfig(t)
+	require.NotEqual(t, common.Address{}, cfg.L1Deployments.ProtocolVersions, "need ProtocolVersions contract deployment")
+	// to speed up the test, make it reload the config more often, and do not impose a long conf depth
+	cfg.Nodes["verifier"].RuntimeConfigReloadInterval = time.Second * 5
+	cfg.Nodes["verifier"].Driver.VerifierConfDepth = 1
+
+	sys, err := cfg.Start(t)
+	require.Nil(t, err, "Error starting up system")
+	defer sys.Close()
+	runtimeConfig := sys.RollupNodes["verifier"].RuntimeConfig()
+
+	// Change the superchain-config via L1
+	l1 := sys.Clients["l1"]
+
+	_, build, major, minor, patch, preRelease := params.OPStackSupport.Parse()
+	newRecommendedProtocolVersion := params.ProtocolVersionV0{Build: build, Major: major + 1, Minor: minor, Patch: patch, PreRelease: preRelease}.Encode()
+	require.NotEqual(t, runtimeConfig.RecommendedProtocolVersion(), newRecommendedProtocolVersion, "changing to a different protocol version")
+
+	protVersions, err := bindings.NewProtocolVersions(cfg.L1Deployments.ProtocolVersionsProxy, l1)
+	require.NoError(t, err)
+
+	// ProtocolVersions contract is owned by same key as SystemConfig in devnet
+	opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.SysCfgOwner, cfg.L1ChainIDBig())
+	require.NoError(t, err)
+
+	// Change recommended protocol version
+	tx, err := protVersions.SetRecommended(opts, new(big.Int).SetBytes(newRecommendedProtocolVersion[:]))
+	require.NoError(t, err)
+
+	// wait for the change to confirm
+	_, err = wait.ForReceiptOK(context.Background(), l1, tx.Hash())
+	require.NoError(t, err)
+
+	// wait for the recommended protocol version to change
+	_, err = retry.Do(context.Background(), 10, retry.Fixed(time.Second*10), func() (struct{}, error) {
+		v := sys.RollupNodes["verifier"].RuntimeConfig().RecommendedProtocolVersion()
+		if v == newRecommendedProtocolVersion {
+			return struct{}{}, nil
+		}
+		return struct{}{}, fmt.Errorf("no change yet, seeing %s but looking for %s", v, newRecommendedProtocolVersion)
+	})
+	require.NoError(t, err)
+}
+
+func TestRequiredProtocolVersionChangeAndHalt(t *testing.T) {
+	InitParallel(t)
+
+	cfg := DefaultSystemConfig(t)
+	// to speed up the test, make it reload the config more often, and do not impose a long conf depth
+	cfg.Nodes["verifier"].RuntimeConfigReloadInterval = time.Second * 5
+	cfg.Nodes["verifier"].Driver.VerifierConfDepth = 1
+	// configure halt in verifier op-node
+	cfg.Nodes["verifier"].RollupHalt = "major"
+	// configure halt in verifier op-geth node
+	cfg.GethOptions["verifier"] = append(cfg.GethOptions["verifier"], []geth.GethOption{
+		func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error {
+			ethCfg.RollupHaltOnIncompatibleProtocolVersion = "major"
+			return nil
+		},
+	}...)
+
+	sys, err := cfg.Start(t)
+	require.Nil(t, err, "Error starting up system")
+	defer sys.Close()
+	runtimeConfig := sys.RollupNodes["verifier"].RuntimeConfig()
+
+	// Change the superchain-config via L1
+	l1 := sys.Clients["l1"]
+
+	_, build, major, minor, patch, preRelease := params.OPStackSupport.Parse()
+	newRequiredProtocolVersion := params.ProtocolVersionV0{Build: build, Major: major + 1, Minor: minor, Patch: patch, PreRelease: preRelease}.Encode()
+	require.NotEqual(t, runtimeConfig.RequiredProtocolVersion(), newRequiredProtocolVersion, "changing to a different protocol version")
+
+	protVersions, err := bindings.NewProtocolVersions(cfg.L1Deployments.ProtocolVersionsProxy, l1)
+	require.NoError(t, err)
+
+	// ProtocolVersions contract is owned by same key as SystemConfig in devnet
+	opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.SysCfgOwner, cfg.L1ChainIDBig())
+	require.NoError(t, err)
+
+	// Change required protocol version
+	tx, err := protVersions.SetRequired(opts, new(big.Int).SetBytes(newRequiredProtocolVersion[:]))
+	require.NoError(t, err)
+
+	// wait for the change to confirm
+	_, err = wait.ForReceiptOK(context.Background(), l1, tx.Hash())
+	require.NoError(t, err)
+
+	// wait for the required protocol version to take effect by halting the verifier that opted in, and halting the op-geth node that opted in.
+	_, err = retry.Do(context.Background(), 10, retry.Fixed(time.Second*10), func() (struct{}, error) {
+		if !sys.RollupNodes["verifier"].Closed() {
+			return struct{}{}, errors.New("verifier rollup node is not closed yet")
+		}
+		return struct{}{}, nil
+	})
+	require.NoError(t, err)
+	t.Log("verified that op-node closed!")
+	// Checking if the engine is down is not trivial in op-e2e.
+	// In op-geth we have halting tests covering the Engine API, in op-e2e we instead check if the API stops.
+	_, err = retry.Do(context.Background(), 10, retry.Fixed(time.Second*10), func() (struct{}, error) {
+		ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
+		_, err := sys.Clients["verifier"].ChainID(ctx)
+		cancel()
+		if err != nil && !errors.Is(err, ctx.Err()) { // waiting for client to stop responding to chainID requests
+			return struct{}{}, nil
+		}
+		return struct{}{}, errors.New("verifier rollup node is not closed yet")
+	})
+	require.NoError(t, err)
+	t.Log("verified that op-geth closed!")
+}
diff --git a/op-node/node/superchain_test.go b/op-node/node/superchain_test.go
new file mode 100644
index 000000000..0e3970333
--- /dev/null
+++ b/op-node/node/superchain_test.go
@@ -0,0 +1,39 @@
+package node
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"golang.org/x/exp/slices"
+
+	"github.com/ethereum/go-ethereum/params"
+)
+
+func TestHaltMaybe(t *testing.T) {
+	haltTest := func(opt string, halts ...params.ProtocolVersionComparison) {
+		t.Run(opt, func(t *testing.T) {
+			for _, h := range []params.ProtocolVersionComparison{
+				params.AheadMajor,
+				params.OutdatedMajor,
+				params.AheadMinor,
+				params.OutdatedMinor,
+				params.AheadPatch,
+				params.OutdatedPatch,
+				params.AheadPrerelease,
+				params.OutdatedPrerelease,
+				params.Matching,
+				params.DiffVersionType,
+				params.DiffBuild,
+				params.EmptyVersion,
+			} {
+				expectedHalt := slices.Contains(halts, h)
+				gotHalt := haltMaybe(opt, h)
+				require.Equal(t, expectedHalt, gotHalt, "%s %d", opt, h)
+			}
+		})
+	}
+	haltTest("")
+	haltTest("major", params.OutdatedMajor)
+	haltTest("minor", params.OutdatedMajor, params.OutdatedMinor)
+	haltTest("patch", params.OutdatedMajor, params.OutdatedMinor, params.OutdatedPatch)
+}
diff --git a/op-service/ioutil/gzip_test.go b/op-service/ioutil/gzip_test.go
index aa5232391..45bdba931 100644
--- a/op-service/ioutil/gzip_test.go
+++ b/op-service/ioutil/gzip_test.go
@@ -1,7 +1,6 @@
 package ioutil
 
 import (
-	"encoding/json"
 	"io"
 	"os"
 	"path/filepath"
@@ -48,43 +47,3 @@ func TestReadWriteWithOptionalCompression(t *testing.T) {
 		})
 	}
 }
-
-func TestWriteReadCompressedJson(t *testing.T) {
-	tests := []struct {
-		name     string
-		filename string
-		err      string
-	}{
-		{"Uncompressed", "test.notgz", "does not have .gz extension"},
-		{"Gzipped", "test.gz", ""},
-	}
-	for _, test := range tests {
-		test := test
-		t.Run(test.name, func(t *testing.T) {
-			dir := t.TempDir()
-			path := filepath.Join(dir, test.filename)
-			err := WriteCompressedJson(path, struct {
-				A int
-				B string
-			}{A: 1, B: "test"})
-			if test.err != "" {
-				require.ErrorContains(t, err, test.err)
-				return
-			}
-			require.NoError(t, err)
-
-			var read struct {
-				A int
-				B string
-			}
-			in, err := OpenDecompressed(path)
-			require.NoError(t, err)
-			err = json.NewDecoder(in).Decode(&read)
-			require.NoError(t, err)
-			require.Equal(t, struct {
-				A int
-				B string
-			}{A: 1, B: "test"}, read)
-		})
-	}
-}
diff --git a/op-service/txmgr/txmgr_test.go b/op-service/txmgr/txmgr_test.go
index ee25507b7..0f16bd938 100644
--- a/op-service/txmgr/txmgr_test.go
+++ b/op-service/txmgr/txmgr_test.go
@@ -93,7 +93,6 @@ type gasPricer struct {
 	mineAtEpoch   int64
 	baseGasTipFee *big.Int
 	baseBaseFee   *big.Int
-	err           error
 	mu            sync.Mutex
 }
 
@@ -207,9 +206,6 @@ func (b *mockBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*typ
 }
 
 func (b *mockBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
-	if b.g.err != nil {
-		return 0, b.g.err
-	}
 	return b.g.basefee().Uint64(), nil
 }
 
@@ -424,31 +420,6 @@ func TestTxMgr_EstimateGas(t *testing.T) {
 	require.Equal(t, gasEstimate, tx.Gas())
 }
 
-func TestTxMgr_EstimateGasFails(t *testing.T) {
-	t.Parallel()
-	h := newTestHarness(t)
-	candidate := h.createTxCandidate()
-
-	// Set the gas limit to zero to trigger gas estimation.
-	candidate.GasLimit = 0
-
-	// Craft a successful transaction.
-	tx, err := h.mgr.craftTx(context.Background(), candidate)
-	require.Nil(t, err)
-	lastNonce := tx.Nonce()
-
-	// Mock gas estimation failure.
-	h.gasPricer.err = fmt.Errorf("execution error")
-	_, err = h.mgr.craftTx(context.Background(), candidate)
-	require.ErrorContains(t, err, "failed to estimate gas")
-
-	// Ensure successful craft uses the correct nonce
-	h.gasPricer.err = nil
-	tx, err = h.mgr.craftTx(context.Background(), candidate)
-	require.Nil(t, err)
-	require.Equal(t, lastNonce+1, tx.Nonce())
-}
-
 // TestTxMgrOnlyOnePublicationSucceeds asserts that the tx manager will return a
 // receipt so long as at least one of the publications is able to succeed with a
 // simulated rpc failure.
diff --git a/packages/contracts-bedrock/test/ProtocolVersions.t.sol b/packages/contracts-bedrock/test/ProtocolVersions.t.sol
index 5c01bafb5..4aa213ad9 100644
--- a/packages/contracts-bedrock/test/ProtocolVersions.t.sol
+++ b/packages/contracts-bedrock/test/ProtocolVersions.t.sol
@@ -60,6 +60,7 @@ contract ProtocolVersions_Initialize_Test is ProtocolVersions_Init {
 
     /// @dev Ensures that the events are emitted during initialization.
     function test_initialize_events_succeeds() external {
+        //vm.store(address(protocolVersions), bytes32(uint256(106)), bytes32(0));
         assertEq(protocolVersionsImpl.owner(), address(0xdEad));
 
         // Wipe out the initialized slot so the proxy can be initialized again