From 0bf2724cf45967a7eae5a53223e154758fecd643 Mon Sep 17 00:00:00 2001
From: Coach Chuck <169060940+coachchucksol@users.noreply.github.com>
Date: Wed, 31 Jul 2024 09:08:37 -0600
Subject: [PATCH] UPDATES: Steward Test Branch to Release (#63)

The big boi, after lots of testing and audits, it's time to release the
Steward.

---------

Co-authored-by: Evan Batsell <ebatsell@gmail.com>
Co-authored-by: Christian <christian.krueger.health@gmail.com>
Co-authored-by: Christian  Krueger <coach@Christians-MacBook-Pro.local>
---
 .github/workflows/build.yaml                  |   8 +-
 Cargo.lock                                    |  43 +-
 Cargo.toml                                    |   2 +-
 keepers/keeper-core/src/lib.rs                |   1 +
 programs/steward/Cargo.toml                   |   2 +
 programs/steward/idl/steward.json             | 869 +++++++++++++-----
 programs/steward/src/constants.rs             |   9 +-
 programs/steward/src/delegation.rs            |  15 +-
 programs/steward/src/errors.rs                |  36 +-
 programs/steward/src/events.rs                |  99 ++
 .../add_validator_to_blacklist.rs             |  10 +-
 .../auto_add_validator_to_pool.rs             | 126 +--
 .../auto_remove_validator_from_pool.rs        | 276 ++++--
 .../instructions/close_steward_accounts.rs    |  30 +
 .../src/instructions/compute_delegations.rs   |  26 +-
 .../instructions/compute_instant_unstake.rs   |  39 +-
 .../steward/src/instructions/compute_score.rs |  49 +-
 .../src/instructions/epoch_maintenance.rs     | 122 +++
 programs/steward/src/instructions/idle.rs     |  30 +-
 .../src/instructions/initialize_state.rs      |  32 -
 ...ialize_config.rs => initialize_steward.rs} |  48 +-
 .../instructions/instant_remove_validator.rs  |  90 ++
 programs/steward/src/instructions/mod.rs      |  14 +-
 .../steward/src/instructions/pause_steward.rs |   4 +-
 .../steward/src/instructions/realloc_state.rs |  18 +-
 .../steward/src/instructions/rebalance.rs     | 185 ++--
 .../remove_validator_from_blacklist.rs        |  13 +-
 .../src/instructions/reset_steward_state.rs   |  69 ++
 .../src/instructions/resume_steward.rs        |   4 +-
 .../src/instructions/set_new_authority.rs     |  77 +-
 .../src/instructions/spl_passthrough.rs       | 294 +++---
 .../src/instructions/update_parameters.rs     |   4 +-
 programs/steward/src/lib.rs                   |  86 +-
 programs/steward/src/score.rs                 |  20 +-
 programs/steward/src/state/accounts.rs        |  58 +-
 programs/steward/src/state/large_bitmask.rs   | 104 +++
 programs/steward/src/state/mod.rs             |   2 +
 programs/steward/src/state/parameters.rs      |   4 +-
 programs/steward/src/state/steward_state.rs   | 425 ++++++---
 programs/steward/src/utils.rs                 | 183 +++-
 programs/validator-history/Cargo.toml         |   2 +
 run_tests.sh                                  |   4 +-
 tests/src/steward_fixtures.rs                 | 147 ++-
 tests/tests/steward/test_algorithms.rs        | 202 ++--
 tests/tests/steward/test_integration.rs       | 238 +++--
 tests/tests/steward/test_parameters.rs        |   7 +-
 tests/tests/steward/test_spl_passthrough.rs   | 104 ++-
 tests/tests/steward/test_state_methods.rs     |  73 +-
 tests/tests/steward/test_state_transitions.rs |  14 +-
 tests/tests/steward/test_steward.rs           | 173 +++-
 utils/steward-cli/Cargo.toml                  |  26 +
 utils/steward-cli/initial_notes.md            | 160 ++++
 .../actions/auto_add_validator_from_pool.rs   | 107 +++
 .../auto_remove_validator_from_pool.rs        | 117 +++
 utils/steward-cli/src/commands/actions/mod.rs |   5 +
 .../commands/actions/remove_bad_validators.rs | 157 ++++
 .../src/commands/actions/reset_state.rs       |  75 ++
 .../src/commands/actions/update_config.rs     |  73 ++
 .../steward-cli/src/commands/command_args.rs  | 367 ++++++++
 .../commands/cranks/compute_delegations.rs    |  78 ++
 .../cranks/compute_instant_unstake.rs         | 118 +++
 .../src/commands/cranks/compute_score.rs      | 125 +++
 .../src/commands/cranks/epoch_maintenance.rs  |  79 ++
 utils/steward-cli/src/commands/cranks/idle.rs |  64 ++
 utils/steward-cli/src/commands/cranks/mod.rs  |   6 +
 .../src/commands/cranks/rebalance.rs          | 144 +++
 utils/steward-cli/src/commands/info/mod.rs    |   3 +
 .../src/commands/info/view_config.rs          | 133 +++
 .../info/view_next_index_to_remove.rs         |  43 +
 .../src/commands/info/view_state.rs           | 173 ++++
 .../src/commands/init/init_state.rs           | 151 +++
 .../src/commands/init/init_steward.rs         | 100 ++
 utils/steward-cli/src/commands/init/mod.rs    |   2 +
 utils/steward-cli/src/commands/mod.rs         |   5 +
 utils/steward-cli/src/main.rs                 |  82 ++
 utils/steward-cli/src/utils/accounts.rs       | 133 +++
 utils/steward-cli/src/utils/mod.rs            |   2 +
 utils/steward-cli/src/utils/transactions.rs   | 113 +++
 78 files changed, 5796 insertions(+), 1335 deletions(-)
 create mode 100644 programs/steward/src/events.rs
 create mode 100644 programs/steward/src/instructions/close_steward_accounts.rs
 create mode 100644 programs/steward/src/instructions/epoch_maintenance.rs
 delete mode 100644 programs/steward/src/instructions/initialize_state.rs
 rename programs/steward/src/instructions/{initialize_config.rs => initialize_steward.rs} (57%)
 create mode 100644 programs/steward/src/instructions/instant_remove_validator.rs
 create mode 100644 programs/steward/src/instructions/reset_steward_state.rs
 create mode 100644 programs/steward/src/state/large_bitmask.rs
 create mode 100644 utils/steward-cli/Cargo.toml
 create mode 100644 utils/steward-cli/initial_notes.md
 create mode 100644 utils/steward-cli/src/commands/actions/auto_add_validator_from_pool.rs
 create mode 100644 utils/steward-cli/src/commands/actions/auto_remove_validator_from_pool.rs
 create mode 100644 utils/steward-cli/src/commands/actions/mod.rs
 create mode 100644 utils/steward-cli/src/commands/actions/remove_bad_validators.rs
 create mode 100644 utils/steward-cli/src/commands/actions/reset_state.rs
 create mode 100644 utils/steward-cli/src/commands/actions/update_config.rs
 create mode 100644 utils/steward-cli/src/commands/command_args.rs
 create mode 100644 utils/steward-cli/src/commands/cranks/compute_delegations.rs
 create mode 100644 utils/steward-cli/src/commands/cranks/compute_instant_unstake.rs
 create mode 100644 utils/steward-cli/src/commands/cranks/compute_score.rs
 create mode 100644 utils/steward-cli/src/commands/cranks/epoch_maintenance.rs
 create mode 100644 utils/steward-cli/src/commands/cranks/idle.rs
 create mode 100644 utils/steward-cli/src/commands/cranks/mod.rs
 create mode 100644 utils/steward-cli/src/commands/cranks/rebalance.rs
 create mode 100644 utils/steward-cli/src/commands/info/mod.rs
 create mode 100644 utils/steward-cli/src/commands/info/view_config.rs
 create mode 100644 utils/steward-cli/src/commands/info/view_next_index_to_remove.rs
 create mode 100644 utils/steward-cli/src/commands/info/view_state.rs
 create mode 100644 utils/steward-cli/src/commands/init/init_state.rs
 create mode 100644 utils/steward-cli/src/commands/init/init_steward.rs
 create mode 100644 utils/steward-cli/src/commands/init/mod.rs
 create mode 100644 utils/steward-cli/src/commands/mod.rs
 create mode 100644 utils/steward-cli/src/main.rs
 create mode 100644 utils/steward-cli/src/utils/accounts.rs
 create mode 100644 utils/steward-cli/src/utils/mod.rs
 create mode 100644 utils/steward-cli/src/utils/transactions.rs

diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 0cf0cc76..9468c272 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -3,11 +3,13 @@ on:
   push:
     branches:
       - master
+      - steward-test-branch
     tags:
       - "v*"
   pull_request:
     branches:
       - master
+      - steward-test-branch
 
 jobs:
   security_audit:
@@ -21,7 +23,7 @@ jobs:
         uses: baptiste0928/cargo-install@v3
         with:
           crate: cargo-audit
-      - run: cargo audit --ignore RUSTSEC-2022-0093 --ignore RUSTSEC-2023-0065 --ignore RUSTSEC-2024-0336 --ignore RUSTSEC-2024-0344
+      - run: cargo audit --ignore RUSTSEC-2022-0093 --ignore RUSTSEC-2023-0065 --ignore RUSTSEC-2024-0336 --ignore RUSTSEC-2024-0344 --ignore RUSTSEC-2024-0357
 
   lint:
     name: lint
@@ -74,7 +76,7 @@ jobs:
         uses: baptiste0928/cargo-install@v3
         with:
           crate: anchor-cli
-          version: "0.30.0"
+          version: "0.30.1"
       - name: install solana toolsuite
         run: sh -c "$(curl -sSfL https://release.solana.com/v1.18.11/install)"
       - name: add to path
@@ -88,6 +90,8 @@ jobs:
       # run verified build
       - run: solana-verify build --library-name validator_history
 
+      - run: solana-verify build --library-name jito_steward -- --features mainnet-beta
+
       # upload the IDL and verified build
       - name: Upload validator_history.so
         uses: actions/upload-artifact@v4
diff --git a/Cargo.lock b/Cargo.lock
index 13282434..1e4b97bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -360,9 +360,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.79"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
 
 [[package]]
 name = "aquamarine"
@@ -1617,6 +1617,12 @@ dependencies = [
  "syn 2.0.48",
 ]
 
+[[package]]
+name = "dotenv"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
+
 [[package]]
 name = "downcast"
 version = "0.11.0"
@@ -6112,6 +6118,31 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 
+[[package]]
+name = "steward-cli"
+version = "0.1.0"
+dependencies = [
+ "anchor-lang",
+ "anyhow",
+ "clap 4.4.18",
+ "dotenv",
+ "futures",
+ "futures-util",
+ "jito-steward",
+ "keeper-core",
+ "log",
+ "solana-account-decoder",
+ "solana-clap-utils",
+ "solana-client",
+ "solana-metrics",
+ "solana-program",
+ "solana-sdk",
+ "spl-stake-pool",
+ "thiserror",
+ "tokio",
+ "validator-history",
+]
+
 [[package]]
 name = "strsim"
 version = "0.8.0"
@@ -6414,9 +6445,9 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.34"
+version = "0.3.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
 dependencies = [
  "deranged",
  "itoa",
@@ -6435,9 +6466,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.17"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
 dependencies = [
  "num-conv",
  "time-core",
diff --git a/Cargo.toml b/Cargo.toml
index d30a17ab..af12b8b0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,7 @@ members = [
   "keepers/*",
   "programs/*",
   "tests",
-  "utils/*"
+  "utils/*",
 ]
 
 [profile.release]
diff --git a/keepers/keeper-core/src/lib.rs b/keepers/keeper-core/src/lib.rs
index dfd0cd58..91351ee0 100644
--- a/keepers/keeper-core/src/lib.rs
+++ b/keepers/keeper-core/src/lib.rs
@@ -394,6 +394,7 @@ pub async fn parallel_execute_transactions(
             // Future optimization: submit these in parallel batches and refresh blockhash for every batch
             match client.send_transaction(tx).await {
                 Ok(signature) => {
+                    println!("Submitted transaction: {:?}", signature);
                     submitted_signatures.insert(signature, idx);
                 }
                 Err(e) => match e.get_transaction_error() {
diff --git a/programs/steward/Cargo.toml b/programs/steward/Cargo.toml
index 874bfcfe..6032572c 100644
--- a/programs/steward/Cargo.toml
+++ b/programs/steward/Cargo.toml
@@ -14,6 +14,8 @@ name = "jito_steward"
 no-entrypoint = []
 no-idl = []
 no-log-ix-name = []
+mainnet-beta = []
+testnet = []
 cpi = ["no-entrypoint"]
 default = ["custom-heap"]
 custom-heap = []
diff --git a/programs/steward/idl/steward.json b/programs/steward/idl/steward.json
index 9ea51b4a..8fce5c22 100644
--- a/programs/steward/idl/steward.json
+++ b/programs/steward/idl/steward.json
@@ -35,13 +35,16 @@
       ],
       "args": [
         {
-          "name": "index",
+          "name": "validator_history_blacklist",
           "type": "u32"
         }
       ]
     },
     {
       "name": "add_validator_to_pool",
+      "docs": [
+        "Passthrough spl-stake-pool: Add a validator to the pool"
+      ],
       "discriminator": [
         181,
         6,
@@ -56,6 +59,10 @@
         {
           "name": "config"
         },
+        {
+          "name": "state_account",
+          "writable": true
+        },
         {
           "name": "stake_pool_program"
         },
@@ -63,9 +70,6 @@
           "name": "stake_pool",
           "writable": true
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "reserve_stake",
           "writable": true
@@ -103,7 +107,7 @@
           "name": "stake_program"
         },
         {
-          "name": "signer",
+          "name": "admin",
           "writable": true,
           "signer": true
         }
@@ -134,21 +138,19 @@
       ],
       "accounts": [
         {
-          "name": "validator_history_account"
+          "name": "config"
         },
         {
-          "name": "config"
+          "name": "steward_state",
+          "writable": true
         },
         {
-          "name": "stake_pool_program"
+          "name": "validator_history_account"
         },
         {
           "name": "stake_pool",
           "writable": true
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "reserve_stake",
           "writable": true
@@ -168,27 +170,25 @@
           "name": "vote_account"
         },
         {
-          "name": "rent"
+          "name": "stake_history"
         },
         {
-          "name": "clock"
+          "name": "stake_config"
         },
         {
-          "name": "stake_history"
+          "name": "stake_program"
         },
         {
-          "name": "stake_config"
+          "name": "stake_pool_program"
         },
         {
           "name": "system_program"
         },
         {
-          "name": "stake_program"
+          "name": "rent"
         },
         {
-          "name": "signer",
-          "writable": true,
-          "signer": true
+          "name": "clock"
         }
       ],
       "args": []
@@ -210,25 +210,19 @@
       ],
       "accounts": [
         {
-          "name": "validator_history_account"
+          "name": "config"
         },
         {
-          "name": "config"
+          "name": "validator_history_account"
         },
         {
           "name": "state_account",
           "writable": true
         },
-        {
-          "name": "stake_pool_program"
-        },
         {
           "name": "stake_pool",
           "writable": true
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "reserve_stake",
           "writable": true
@@ -255,27 +249,25 @@
           "name": "vote_account"
         },
         {
-          "name": "rent"
+          "name": "stake_history"
         },
         {
-          "name": "clock"
+          "name": "stake_config"
         },
         {
-          "name": "stake_history"
+          "name": "stake_program"
         },
         {
-          "name": "stake_config"
+          "name": "stake_pool_program"
         },
         {
           "name": "system_program"
         },
         {
-          "name": "stake_program"
+          "name": "rent"
         },
         {
-          "name": "signer",
-          "writable": true,
-          "signer": true
+          "name": "clock"
         }
       ],
       "args": [
@@ -285,6 +277,39 @@
         }
       ]
     },
+    {
+      "name": "close_steward_accounts",
+      "docs": [
+        "Closes Steward PDA accounts associated with a given Config (StewardStateAccount, and Staker).",
+        "Config is not closed as it is a Keypair, so lamports can simply be withdrawn.",
+        "Reclaims lamports to authority"
+      ],
+      "discriminator": [
+        172,
+        171,
+        212,
+        186,
+        90,
+        10,
+        181,
+        24
+      ],
+      "accounts": [
+        {
+          "name": "config"
+        },
+        {
+          "name": "state_account",
+          "writable": true
+        },
+        {
+          "name": "authority",
+          "writable": true,
+          "signer": true
+        }
+      ],
+      "args": []
+    },
     {
       "name": "compute_delegations",
       "docs": [
@@ -310,9 +335,7 @@
           "writable": true
         },
         {
-          "name": "signer",
-          "writable": true,
-          "signer": true
+          "name": "validator_list"
         }
       ],
       "args": []
@@ -348,11 +371,6 @@
         },
         {
           "name": "cluster_history"
-        },
-        {
-          "name": "signer",
-          "writable": true,
-          "signer": true
         }
       ],
       "args": [
@@ -393,11 +411,6 @@
         },
         {
           "name": "cluster_history"
-        },
-        {
-          "name": "signer",
-          "writable": true,
-          "signer": true
         }
       ],
       "args": [
@@ -409,6 +422,9 @@
     },
     {
       "name": "decrease_additional_validator_stake",
+      "docs": [
+        "Passthrough spl-stake-pool: Decrease additional validator stake"
+      ],
       "discriminator": [
         90,
         22,
@@ -424,7 +440,7 @@
           "name": "config"
         },
         {
-          "name": "steward_state",
+          "name": "state_account",
           "writable": true
         },
         {
@@ -440,9 +456,6 @@
         {
           "name": "stake_pool"
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "withdraw_authority"
         },
@@ -479,7 +492,7 @@
           "name": "stake_program"
         },
         {
-          "name": "signer",
+          "name": "admin",
           "writable": true,
           "signer": true
         }
@@ -501,6 +514,9 @@
     },
     {
       "name": "decrease_validator_stake",
+      "docs": [
+        "Passthrough spl-stake-pool: Decrease validator stake"
+      ],
       "discriminator": [
         145,
         203,
@@ -516,7 +532,7 @@
           "name": "config"
         },
         {
-          "name": "steward_state",
+          "name": "state_account",
           "writable": true
         },
         {
@@ -530,9 +546,6 @@
           "name": "stake_pool",
           "writable": true
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "withdraw_authority"
         },
@@ -571,7 +584,7 @@
           "name": "stake_program"
         },
         {
-          "name": "signer",
+          "name": "admin",
           "writable": true,
           "signer": true
         }
@@ -587,6 +600,45 @@
         }
       ]
     },
+    {
+      "name": "epoch_maintenance",
+      "docs": [
+        "Housekeeping, run at the start of any new epoch before any other instructions"
+      ],
+      "discriminator": [
+        208,
+        225,
+        211,
+        82,
+        219,
+        242,
+        58,
+        200
+      ],
+      "accounts": [
+        {
+          "name": "config"
+        },
+        {
+          "name": "state_account",
+          "writable": true
+        },
+        {
+          "name": "validator_list"
+        },
+        {
+          "name": "stake_pool"
+        }
+      ],
+      "args": [
+        {
+          "name": "validator_index_to_remove",
+          "type": {
+            "option": "u64"
+          }
+        }
+      ]
+    },
     {
       "name": "idle",
       "docs": [
@@ -611,15 +663,16 @@
           "writable": true
         },
         {
-          "name": "signer",
-          "writable": true,
-          "signer": true
+          "name": "validator_list"
         }
       ],
       "args": []
     },
     {
       "name": "increase_additional_validator_stake",
+      "docs": [
+        "Passthrough spl-stake-pool: Increase additional validator stake"
+      ],
       "discriminator": [
         93,
         136,
@@ -635,7 +688,7 @@
           "name": "config"
         },
         {
-          "name": "steward_state",
+          "name": "state_account",
           "writable": true
         },
         {
@@ -648,9 +701,6 @@
         {
           "name": "stake_pool"
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "withdraw_authority"
         },
@@ -692,7 +742,7 @@
           "name": "stake_program"
         },
         {
-          "name": "signer",
+          "name": "admin",
           "writable": true,
           "signer": true
         }
@@ -714,6 +764,9 @@
     },
     {
       "name": "increase_validator_stake",
+      "docs": [
+        "Passthrough spl-stake-pool: Increase validator stake"
+      ],
       "discriminator": [
         5,
         121,
@@ -729,7 +782,7 @@
           "name": "config"
         },
         {
-          "name": "steward_state",
+          "name": "state_account",
           "writable": true
         },
         {
@@ -743,9 +796,6 @@
           "name": "stake_pool",
           "writable": true
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "withdraw_authority"
         },
@@ -787,7 +837,7 @@
           "name": "stake_program"
         },
         {
-          "name": "signer",
+          "name": "admin",
           "writable": true,
           "signer": true
         }
@@ -804,16 +854,16 @@
       ]
     },
     {
-      "name": "initialize_config",
+      "name": "initialize_steward",
       "discriminator": [
-        208,
-        127,
-        21,
-        1,
-        194,
-        190,
-        196,
-        70
+        195,
+        182,
+        16,
+        84,
+        217,
+        58,
+        220,
+        175
       ],
       "accounts": [
         {
@@ -822,7 +872,7 @@
           "signer": true
         },
         {
-          "name": "staker",
+          "name": "state_account",
           "writable": true
         },
         {
@@ -836,16 +886,12 @@
           "name": "system_program"
         },
         {
-          "name": "signer",
+          "name": "current_staker",
           "writable": true,
           "signer": true
         }
       ],
       "args": [
-        {
-          "name": "authority",
-          "type": "pubkey"
-        },
         {
           "name": "update_parameters_args",
           "type": {
@@ -857,41 +903,47 @@
       ]
     },
     {
-      "name": "initialize_state",
+      "name": "instant_remove_validator",
       "docs": [
-        "Creates state account"
+        "When a validator is marked for immediate removal, it needs to be removed before normal functions can continue"
       ],
       "discriminator": [
-        190,
-        171,
-        224,
-        219,
-        217,
-        72,
-        199,
-        176
+        119,
+        127,
+        216,
+        135,
+        24,
+        63,
+        229,
+        242
       ],
       "accounts": [
         {
-          "name": "state_account",
-          "writable": true
+          "name": "config"
         },
         {
-          "name": "config"
+          "name": "state_account",
+          "writable": true
         },
         {
-          "name": "system_program"
+          "name": "validator_list"
         },
         {
-          "name": "signer",
-          "writable": true,
-          "signer": true
+          "name": "stake_pool"
         }
       ],
-      "args": []
+      "args": [
+        {
+          "name": "validator_index_to_remove",
+          "type": "u64"
+        }
+      ]
     },
     {
       "name": "pause_steward",
+      "docs": [
+        "Pauses the steward, preventing any further state transitions"
+      ],
       "discriminator": [
         214,
         85,
@@ -985,10 +1037,6 @@
         {
           "name": "stake_pool"
         },
-        {
-          "name": "staker",
-          "writable": true
-        },
         {
           "name": "withdraw_authority"
         },
@@ -1031,11 +1079,6 @@
         },
         {
           "name": "stake_program"
-        },
-        {
-          "name": "signer",
-          "writable": true,
-          "signer": true
         }
       ],
       "args": [
@@ -1073,13 +1116,16 @@
       ],
       "args": [
         {
-          "name": "index",
+          "name": "validator_history_blacklist",
           "type": "u32"
         }
       ]
     },
     {
       "name": "remove_validator_from_pool",
+      "docs": [
+        "Passthrough spl-stake-pool: Remove a validator from the pool"
+      ],
       "discriminator": [
         161,
         32,
@@ -1095,7 +1141,7 @@
           "name": "config"
         },
         {
-          "name": "steward_state",
+          "name": "state_account",
           "writable": true
         },
         {
@@ -1105,9 +1151,6 @@
           "name": "stake_pool",
           "writable": true
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "withdraw_authority"
         },
@@ -1133,7 +1176,7 @@
           "name": "stake_program"
         },
         {
-          "name": "signer",
+          "name": "admin",
           "writable": true,
           "signer": true
         }
@@ -1145,8 +1188,48 @@
         }
       ]
     },
+    {
+      "name": "reset_steward_state",
+      "docs": [
+        "Resets steward state account to its initial state."
+      ],
+      "discriminator": [
+        84,
+        248,
+        158,
+        46,
+        200,
+        205,
+        234,
+        86
+      ],
+      "accounts": [
+        {
+          "name": "state_account",
+          "writable": true
+        },
+        {
+          "name": "config"
+        },
+        {
+          "name": "stake_pool"
+        },
+        {
+          "name": "validator_list"
+        },
+        {
+          "name": "authority",
+          "writable": true,
+          "signer": true
+        }
+      ],
+      "args": []
+    },
     {
       "name": "resume_steward",
+      "docs": [
+        "Resumes the steward, allowing state transitions to continue"
+      ],
       "discriminator": [
         25,
         71,
@@ -1191,15 +1274,27 @@
           "name": "new_authority"
         },
         {
-          "name": "authority",
+          "name": "admin",
           "writable": true,
           "signer": true
         }
       ],
-      "args": []
-    },
+      "args": [
+        {
+          "name": "authority_type",
+          "type": {
+            "defined": {
+              "name": "AuthorityType"
+            }
+          }
+        }
+      ]
+    },
     {
       "name": "set_preferred_validator",
+      "docs": [
+        "Passthrough spl-stake-pool: Set the preferred validator"
+      ],
       "discriminator": [
         114,
         42,
@@ -1214,6 +1309,10 @@
         {
           "name": "config"
         },
+        {
+          "name": "state_account",
+          "writable": true
+        },
         {
           "name": "stake_pool_program"
         },
@@ -1221,14 +1320,11 @@
           "name": "stake_pool",
           "writable": true
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "validator_list"
         },
         {
-          "name": "signer",
+          "name": "admin",
           "writable": true,
           "signer": true
         }
@@ -1252,6 +1348,9 @@
     },
     {
       "name": "set_staker",
+      "docs": [
+        "Passthrough spl-stake-pool: Set the staker for the pool"
+      ],
       "discriminator": [
         149,
         203,
@@ -1266,6 +1365,10 @@
         {
           "name": "config"
         },
+        {
+          "name": "state_account",
+          "writable": true
+        },
         {
           "name": "stake_pool_program"
         },
@@ -1273,14 +1376,11 @@
           "name": "stake_pool",
           "writable": true
         },
-        {
-          "name": "staker"
-        },
         {
           "name": "new_staker"
         },
         {
-          "name": "signer",
+          "name": "admin",
           "writable": true,
           "signer": true
         }
@@ -1352,19 +1452,6 @@
         130
       ]
     },
-    {
-      "name": "Staker",
-      "discriminator": [
-        171,
-        229,
-        193,
-        85,
-        67,
-        177,
-        151,
-        4
-      ]
-    },
     {
       "name": "StewardStateAccount",
       "discriminator": [
@@ -1393,6 +1480,32 @@
     }
   ],
   "events": [
+    {
+      "name": "AutoAddValidatorEvent",
+      "discriminator": [
+        123,
+        65,
+        239,
+        15,
+        82,
+        216,
+        206,
+        28
+      ]
+    },
+    {
+      "name": "AutoRemoveValidatorEvent",
+      "discriminator": [
+        211,
+        46,
+        52,
+        163,
+        17,
+        38,
+        197,
+        186
+      ]
+    },
     {
       "name": "DecreaseComponents",
       "discriminator": [
@@ -1406,6 +1519,19 @@
         8
       ]
     },
+    {
+      "name": "EpochMaintenanceEvent",
+      "discriminator": [
+        255,
+        149,
+        70,
+        161,
+        199,
+        176,
+        9,
+        42
+      ]
+    },
     {
       "name": "InstantUnstakeComponents",
       "discriminator": [
@@ -1419,6 +1545,19 @@
         77
       ]
     },
+    {
+      "name": "RebalanceEvent",
+      "discriminator": [
+        120,
+        27,
+        117,
+        235,
+        104,
+        42,
+        132,
+        75
+      ]
+    },
     {
       "name": "ScoreComponents",
       "discriminator": [
@@ -1449,120 +1588,241 @@
   "errors": [
     {
       "code": 6000,
-      "name": "ScoringNotComplete",
-      "msg": "Scoring must be completed before any other steps can be taken"
+      "name": "InvalidAuthorityType",
+      "msg": "Invalid set authority type: 0: SetAdmin, 1: SetBlacklistAuthority, 2: SetParametersAuthority"
     },
     {
       "code": 6001,
-      "name": "ValidatorNotInList",
-      "msg": "Validator does not exist at the ValidatorList index provided"
+      "name": "ScoringNotComplete",
+      "msg": "Scoring must be completed before any other steps can be taken"
     },
     {
       "code": 6002,
-      "name": "AddValidatorsNotComplete",
-      "msg": "Add validators step must be completed before any other steps can be taken"
+      "name": "ValidatorNotInList",
+      "msg": "Validator does not exist at the ValidatorList index provided"
     },
     {
       "code": 6003,
-      "name": "EpochNotOver",
-      "msg": "Cannot reset state before epoch is over"
-    },
-    {
-      "code": 6004,
       "name": "Unauthorized",
       "msg": "Unauthorized to perform this action"
     },
     {
-      "code": 6005,
+      "code": 6004,
       "name": "BitmaskOutOfBounds",
       "msg": "Bitmask index out of bounds"
     },
     {
-      "code": 6006,
-      "name": "StateNotReset",
-      "msg": "Epoch state not reset"
-    },
-    {
-      "code": 6007,
-      "name": "ValidatorOutOfRange",
-      "msg": "Validator History created after epoch start, out of range"
+      "code": 6005,
+      "name": "InvalidState",
+      "msg": "Invalid state"
     },
     {
-      "code": 6008,
-      "name": "InvalidState"
+      "code": 6006,
+      "name": "StakeStateIsNotStake",
+      "msg": "Stake state is not Stake"
     },
     {
-      "code": 6009,
+      "code": 6007,
       "name": "ValidatorBelowStakeMinimum",
       "msg": "Validator not eligible to be added to the pool. Must meet stake minimum"
     },
     {
-      "code": 6010,
+      "code": 6008,
       "name": "ValidatorBelowLivenessMinimum",
       "msg": "Validator not eligible to be added to the pool. Must meet recent voting minimum"
     },
     {
-      "code": 6011,
+      "code": 6009,
       "name": "VoteHistoryNotRecentEnough",
       "msg": "Validator History vote data not recent enough to be used for scoring. Must be updated this epoch"
     },
     {
-      "code": 6012,
+      "code": 6010,
       "name": "StakeHistoryNotRecentEnough",
       "msg": "Validator History stake data not recent enough to be used for scoring. Must be updated this epoch"
     },
     {
-      "code": 6013,
+      "code": 6011,
       "name": "ClusterHistoryNotRecentEnough",
       "msg": "Cluster History data not recent enough to be used for scoring. Must be updated this epoch"
     },
     {
-      "code": 6014,
+      "code": 6012,
       "name": "StateMachinePaused",
       "msg": "Steward State Machine is paused. No state machine actions can be taken"
     },
     {
-      "code": 6015,
+      "code": 6013,
       "name": "InvalidParameterValue",
       "msg": "Config parameter is out of range or otherwise invalid"
     },
     {
-      "code": 6016,
+      "code": 6014,
       "name": "InstantUnstakeNotReady",
       "msg": "Instant unstake cannot be performed yet."
     },
     {
-      "code": 6017,
+      "code": 6015,
       "name": "ValidatorIndexOutOfBounds",
       "msg": "Validator index out of bounds of state machine"
     },
     {
-      "code": 6018,
+      "code": 6016,
       "name": "ValidatorListTypeMismatch",
       "msg": "ValidatorList account type mismatch"
     },
     {
-      "code": 6019,
+      "code": 6017,
       "name": "ArithmeticError",
       "msg": "An operation caused an overflow/underflow"
     },
     {
-      "code": 6020,
+      "code": 6018,
       "name": "ValidatorNotRemovable",
       "msg": "Validator not eligible for removal. Must be delinquent or have closed vote account"
     },
     {
-      "code": 6021,
+      "code": 6019,
+      "name": "ValidatorMarkedActive",
+      "msg": "Validator was marked active when it should be deactivating"
+    },
+    {
+      "code": 6020,
       "name": "MaxValidatorsReached",
       "msg": "Max validators reached"
     },
+    {
+      "code": 6021,
+      "name": "EpochMaintenanceNotComplete",
+      "msg": "Epoch Maintenance must be called before continuing"
+    },
     {
       "code": 6022,
-      "name": "ValidatorHistoryMismatch",
-      "msg": "Validator history account does not match vote account"
+      "name": "StakePoolNotUpdated",
+      "msg": "The stake pool must be updated before continuing"
+    },
+    {
+      "code": 6023,
+      "name": "EpochMaintenanceAlreadyComplete",
+      "msg": "Epoch Maintenance has already been completed"
+    },
+    {
+      "code": 6024,
+      "name": "ValidatorsNeedToBeRemoved",
+      "msg": "Validators are marked for immediate removal"
+    },
+    {
+      "code": 6025,
+      "name": "ValidatorNotMarkedForRemoval",
+      "msg": "Validator not marked for removal"
+    },
+    {
+      "code": 6026,
+      "name": "ValidatorsHaveNotBeenRemoved",
+      "msg": "Validators have not been removed"
+    },
+    {
+      "code": 6027,
+      "name": "ListStateMismatch",
+      "msg": "Validator List count does not match state machine"
+    },
+    {
+      "code": 6028,
+      "name": "VoteAccountDoesNotMatch",
+      "msg": "Vote account does not match"
+    },
+    {
+      "code": 6029,
+      "name": "ValidatorNeedsToBeMarkedForRemoval",
+      "msg": "Validator needs to be marked for removal"
     }
   ],
   "types": [
+    {
+      "name": "AuthorityType",
+      "type": {
+        "kind": "enum",
+        "variants": [
+          {
+            "name": "SetAdmin",
+            "fields": [
+              {
+                "name": "SetAdmin",
+                "type": {
+                  "option": "u8"
+                }
+              }
+            ]
+          },
+          {
+            "name": "SetBlacklistAuthority",
+            "fields": [
+              {
+                "name": "SetBlacklistAuthority",
+                "type": {
+                  "option": "u8"
+                }
+              }
+            ]
+          },
+          {
+            "name": "SetParameterAuthority",
+            "fields": [
+              {
+                "name": "SetParameterAuthority",
+                "type": {
+                  "option": "u8"
+                }
+              }
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "name": "AutoAddValidatorEvent",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "validator_list_index",
+            "type": "u64"
+          },
+          {
+            "name": "vote_account",
+            "type": "pubkey"
+          }
+        ]
+      }
+    },
+    {
+      "name": "AutoRemoveValidatorEvent",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "validator_list_index",
+            "type": "u64"
+          },
+          {
+            "name": "vote_account",
+            "type": "pubkey"
+          },
+          {
+            "name": "vote_account_closed",
+            "type": "bool"
+          },
+          {
+            "name": "stake_account_deactivated",
+            "type": "bool"
+          },
+          {
+            "name": "marked_for_immediate_removal",
+            "type": "bool"
+          }
+        ]
+      }
+    },
     {
       "name": "BitMask",
       "docs": [
@@ -1812,20 +2072,49 @@
             "type": "pubkey"
           },
           {
-            "name": "authority",
+            "name": "validator_list",
+            "docs": [
+              "Validator List"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "admin",
+            "docs": [
+              "Admin",
+              "- Update the `parameters_authority`",
+              "- Update the `blacklist_authority`",
+              "- Can call SPL Passthrough functions",
+              "- Can pause/reset the state machine"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "parameters_authority",
             "docs": [
-              "Authority for pool stewardship, can execute SPL Staker commands and adjust Delegation parameters"
+              "Parameters Authority",
+              "- Can update steward parameters"
             ],
             "type": "pubkey"
           },
           {
-            "name": "blacklist",
+            "name": "blacklist_authority",
             "docs": [
-              "Bitmask representing index of validators that are not allowed delegation"
+              "Blacklist Authority",
+              "- Can add to the blacklist",
+              "- Can remove from the blacklist"
+            ],
+            "type": "pubkey"
+          },
+          {
+            "name": "validator_history_blacklist",
+            "docs": [
+              "Bitmask representing index of validators that are not allowed delegation",
+              "NOTE: This is indexed off of the validator history, NOT the validator list"
             ],
             "type": {
               "defined": {
-                "name": "BitMask"
+                "name": "LargeBitMask"
               }
             }
           },
@@ -1841,26 +2130,26 @@
             }
           },
           {
-            "name": "_padding",
+            "name": "paused",
             "docs": [
-              "Padding for future governance parameters"
+              "Halts any state machine progress"
             ],
             "type": {
-              "array": [
-                "u8",
-                1023
-              ]
+              "defined": {
+                "name": "U8Bool"
+              }
             }
           },
           {
-            "name": "paused",
+            "name": "_padding",
             "docs": [
-              "Halts any state machine progress"
+              "Padding for future governance parameters"
             ],
             "type": {
-              "defined": {
-                "name": "U8Bool"
-              }
+              "array": [
+                "u8",
+                1023
+              ]
             }
           }
         ]
@@ -1910,6 +2199,40 @@
         ]
       }
     },
+    {
+      "name": "EpochMaintenanceEvent",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "validator_index_to_remove",
+            "type": {
+              "option": "u64"
+            }
+          },
+          {
+            "name": "validator_list_length",
+            "type": "u64"
+          },
+          {
+            "name": "num_pool_validators",
+            "type": "u64"
+          },
+          {
+            "name": "validators_to_remove",
+            "type": "u64"
+          },
+          {
+            "name": "validators_to_add",
+            "type": "u64"
+          },
+          {
+            "name": "maintenance_complete",
+            "type": "bool"
+          }
+        ]
+      }
+    },
     {
       "name": "InstantUnstakeComponents",
       "type": {
@@ -1946,7 +2269,7 @@
           {
             "name": "is_blacklisted",
             "docs": [
-              "Checks if validator was added to blacklist blacklisted"
+              "Checks if validator was added to blacklist"
             ],
             "type": "bool"
           },
@@ -1961,6 +2284,33 @@
         ]
       }
     },
+    {
+      "name": "LargeBitMask",
+      "docs": [
+        "Data structure used to efficiently pack a binary array, primarily used to store all validators.",
+        "Each validator has an index (its index in the spl_stake_pool::ValidatorList), corresponding to a bit in the bitmask.",
+        "When an operation is executed on a validator, the bit corresponding to that validator's index is set to 1.",
+        "When all bits are 1, the operation is complete."
+      ],
+      "serialization": "bytemuck",
+      "repr": {
+        "kind": "c"
+      },
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "values",
+            "type": {
+              "array": [
+                "u64",
+                313
+              ]
+            }
+          }
+        ]
+      }
+    },
     {
       "name": "Parameters",
       "serialization": "bytemuck",
@@ -2027,7 +2377,7 @@
             "type": "u8"
           },
           {
-            "name": "padding0",
+            "name": "_padding_0",
             "docs": [
               "Required so that the struct is 8-byte aligned",
               "https://doc.rust-lang.org/reference/type-layout.html#reprc-structs"
@@ -2105,6 +2455,15 @@
               "Minimum epochs voting required to be in the pool ValidatorList and eligible for delegation"
             ],
             "type": "u64"
+          },
+          {
+            "name": "_padding_1",
+            "type": {
+              "array": [
+                "u64",
+                32
+              ]
+            }
           }
         ]
       }
@@ -2123,6 +2482,59 @@
         ]
       }
     },
+    {
+      "name": "RebalanceEvent",
+      "type": {
+        "kind": "struct",
+        "fields": [
+          {
+            "name": "vote_account",
+            "type": "pubkey"
+          },
+          {
+            "name": "epoch",
+            "type": "u16"
+          },
+          {
+            "name": "rebalance_type_tag",
+            "type": {
+              "defined": {
+                "name": "RebalanceTypeTag"
+              }
+            }
+          },
+          {
+            "name": "increase_lamports",
+            "type": "u64"
+          },
+          {
+            "name": "decrease_components",
+            "type": {
+              "defined": {
+                "name": "DecreaseComponents"
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "name": "RebalanceTypeTag",
+      "type": {
+        "kind": "enum",
+        "variants": [
+          {
+            "name": "None"
+          },
+          {
+            "name": "Increase"
+          },
+          {
+            "name": "Decrease"
+          }
+        ]
+      }
+    },
     {
       "name": "ScoreComponents",
       "type": {
@@ -2210,18 +2622,6 @@
         ]
       }
     },
-    {
-      "name": "Staker",
-      "type": {
-        "kind": "struct",
-        "fields": [
-          {
-            "name": "bump",
-            "type": "u8"
-          }
-        ]
-      }
-    },
     {
       "name": "StateTransition",
       "type": {
@@ -2250,7 +2650,8 @@
       "name": "StewardState",
       "docs": [
         "Tracks state of the stake pool.",
-        "Follow state transitions here: [TODO add link to github diagram]"
+        "Follow state transitions here:",
+        "https://github.com/jito-foundation/stakenet/blob/master/programs/steward/state-machine-diagram.png"
       ],
       "serialization": "bytemuck",
       "repr": {
@@ -2369,6 +2770,30 @@
               }
             }
           },
+          {
+            "name": "validators_for_immediate_removal",
+            "docs": [
+              "Marks a validator for immediate removal after `remove_validator_from_pool` has been called on the stake pool",
+              "This happens when a validator is able to be removed within the same epoch as it was marked"
+            ],
+            "type": {
+              "defined": {
+                "name": "BitMask"
+              }
+            }
+          },
+          {
+            "name": "validators_to_remove",
+            "docs": [
+              "Marks a validator for removal after `remove_validator_from_pool` has been called on the stake pool",
+              "This is cleaned up in the next epoch"
+            ],
+            "type": {
+              "defined": {
+                "name": "BitMask"
+              }
+            }
+          },
           {
             "name": "start_computing_scores_slot",
             "docs": [
@@ -2420,26 +2845,18 @@
             "type": "u64"
           },
           {
-            "name": "compute_delegations_completed",
+            "name": "status_flags",
             "docs": [
-              "Tracks whether delegation computation has been completed"
+              "Flags to track state transitions and operations"
             ],
-            "type": {
-              "defined": {
-                "name": "U8Bool"
-              }
-            }
+            "type": "u32"
           },
           {
-            "name": "rebalance_completed",
+            "name": "validators_added",
             "docs": [
-              "Tracks whether unstake and delegate steps have completed"
+              "Number of validators added to the pool in the current cycle"
             ],
-            "type": {
-              "defined": {
-                "name": "U8Bool"
-              }
-            }
+            "type": "u16"
           },
           {
             "name": "_padding0",
@@ -2449,7 +2866,7 @@
             "type": {
               "array": [
                 "u8",
-                40006
+                40002
               ]
             }
           }
diff --git a/programs/steward/src/constants.rs b/programs/steward/src/constants.rs
index 1bacb9c5..e79bf67b 100644
--- a/programs/steward/src/constants.rs
+++ b/programs/steward/src/constants.rs
@@ -1,8 +1,11 @@
-pub const MAX_ALLOC_BYTES: usize = 10240;
+pub const MAX_ALLOC_BYTES: usize = 10_240;
+pub const VEC_SIZE_BYTES: usize = 4;
+pub const U64_SIZE: usize = 8;
+pub const STAKE_STATUS_OFFSET: usize = 40;
 pub const STAKE_POOL_WITHDRAW_SEED: &[u8] = b"withdraw";
 pub const STAKE_POOL_TRANSIENT_SEED: &[u8] = b"transient";
-pub const MAX_VALIDATORS: usize = 5000;
-pub const BASIS_POINTS_MAX: u16 = 10000;
+pub const MAX_VALIDATORS: usize = 5_000;
+pub const BASIS_POINTS_MAX: u16 = 10_000;
 pub const COMMISSION_MAX: u8 = 100;
 pub const SORTED_INDEX_DEFAULT: u16 = u16::MAX;
 // Need at least 1% of slots remaining (4320 slots) to execute steps in state machine
diff --git a/programs/steward/src/delegation.rs b/programs/steward/src/delegation.rs
index 2ea078ae..e5786d4f 100644
--- a/programs/steward/src/delegation.rs
+++ b/programs/steward/src/delegation.rs
@@ -1,28 +1,20 @@
 use anchor_lang::prelude::*;
 use spl_stake_pool::big_vec::BigVec;
 
+use crate::events::DecreaseComponents;
 use crate::{
     errors::StewardError,
     utils::{get_target_lamports, stake_lamports_at_validator_list_index},
     StewardState,
 };
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub enum RebalanceType {
     Increase(u64),
     Decrease(DecreaseComponents),
     None,
 }
 
-#[event]
-#[derive(Debug, PartialEq, Eq)]
-pub struct DecreaseComponents {
-    pub scoring_unstake_lamports: u64,
-    pub instant_unstake_lamports: u64,
-    pub stake_deposit_unstake_lamports: u64,
-    pub total_unstake_lamports: u64,
-}
-
 /// Given a target validator, determines how much stake to remove on this validator given the constraints of unstaking caps.
 /// Validators with lower yield_scores are prioritized for unstaking. We simulate unstaking movements on each validator, starting
 /// from the lowest yield_score validator, until we reach the target validator. If the target validator is reached and there is still
@@ -168,7 +160,8 @@ pub fn increase_stake_calculation(
         }
         return Err(StewardError::ValidatorIndexOutOfBounds.into());
     }
-    Err(StewardError::InvalidState.into())
+
+    Err(StewardError::ValidatorIndexOutOfBounds.into())
 }
 
 #[derive(Default)]
diff --git a/programs/steward/src/errors.rs b/programs/steward/src/errors.rs
index 7380a4ef..d1c0fe69 100644
--- a/programs/steward/src/errors.rs
+++ b/programs/steward/src/errors.rs
@@ -2,24 +2,20 @@ use anchor_lang::prelude::*;
 
 #[error_code]
 pub enum StewardError {
+    #[msg("Invalid set authority type: 0: SetAdmin, 1: SetBlacklistAuthority, 2: SetParametersAuthority")]
+    InvalidAuthorityType,
     #[msg("Scoring must be completed before any other steps can be taken")]
     ScoringNotComplete,
     #[msg("Validator does not exist at the ValidatorList index provided")]
     ValidatorNotInList,
-    #[msg("Add validators step must be completed before any other steps can be taken")]
-    AddValidatorsNotComplete,
-    #[msg("Cannot reset state before epoch is over")]
-    EpochNotOver,
     #[msg("Unauthorized to perform this action")]
     Unauthorized,
     #[msg("Bitmask index out of bounds")]
     BitmaskOutOfBounds,
-    #[msg("Epoch state not reset")]
-    StateNotReset,
-    #[msg("Validator History created after epoch start, out of range")]
-    ValidatorOutOfRange,
-    // Use invalid_state_error method to ensure expected and actual are logged
+    #[msg("Invalid state")]
     InvalidState,
+    #[msg("Stake state is not Stake")]
+    StakeStateIsNotStake,
     #[msg("Validator not eligible to be added to the pool. Must meet stake minimum")]
     ValidatorBelowStakeMinimum,
     #[msg("Validator not eligible to be added to the pool. Must meet recent voting minimum")]
@@ -46,8 +42,26 @@ pub enum StewardError {
     ArithmeticError,
     #[msg("Validator not eligible for removal. Must be delinquent or have closed vote account")]
     ValidatorNotRemovable,
+    #[msg("Validator was marked active when it should be deactivating")]
+    ValidatorMarkedActive,
     #[msg("Max validators reached")]
     MaxValidatorsReached,
-    #[msg("Validator history account does not match vote account")]
-    ValidatorHistoryMismatch,
+    #[msg("Epoch Maintenance must be called before continuing")]
+    EpochMaintenanceNotComplete,
+    #[msg("The stake pool must be updated before continuing")]
+    StakePoolNotUpdated,
+    #[msg("Epoch Maintenance has already been completed")]
+    EpochMaintenanceAlreadyComplete,
+    #[msg("Validators are marked for immediate removal")]
+    ValidatorsNeedToBeRemoved,
+    #[msg("Validator not marked for removal")]
+    ValidatorNotMarkedForRemoval,
+    #[msg("Validators have not been removed")]
+    ValidatorsHaveNotBeenRemoved,
+    #[msg("Validator List count does not match state machine")]
+    ListStateMismatch,
+    #[msg("Vote account does not match")]
+    VoteAccountDoesNotMatch,
+    #[msg("Validator needs to be marked for removal")]
+    ValidatorNeedsToBeMarkedForRemoval,
 }
diff --git a/programs/steward/src/events.rs b/programs/steward/src/events.rs
new file mode 100644
index 00000000..d85683d7
--- /dev/null
+++ b/programs/steward/src/events.rs
@@ -0,0 +1,99 @@
+use anchor_lang::idl::{
+    types::{IdlEnumVariant, IdlTypeDef, IdlTypeDefTy},
+    IdlBuild,
+};
+use anchor_lang::prelude::{event, AnchorDeserialize, AnchorSerialize};
+use anchor_lang::solana_program::pubkey::Pubkey;
+use borsh::{BorshDeserialize, BorshSerialize};
+
+#[event]
+#[derive(Debug, Clone)]
+
+pub struct AutoRemoveValidatorEvent {
+    pub validator_list_index: u64,
+    pub vote_account: Pubkey,
+    pub vote_account_closed: bool,
+    pub stake_account_deactivated: bool,
+    pub marked_for_immediate_removal: bool,
+}
+
+#[event]
+#[derive(Debug, Clone)]
+pub struct AutoAddValidatorEvent {
+    pub validator_list_index: u64,
+    pub vote_account: Pubkey,
+}
+
+#[event]
+#[derive(Debug, Clone)]
+pub struct EpochMaintenanceEvent {
+    pub validator_index_to_remove: Option<u64>,
+    pub validator_list_length: u64,
+    pub num_pool_validators: u64,
+    pub validators_to_remove: u64,
+    pub validators_to_add: u64,
+    pub maintenance_complete: bool,
+}
+
+#[event]
+#[derive(Debug, Clone)]
+pub struct StateTransition {
+    pub epoch: u64,
+    pub slot: u64,
+    pub previous_state: String,
+    pub new_state: String,
+}
+
+#[event]
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct DecreaseComponents {
+    pub scoring_unstake_lamports: u64,
+    pub instant_unstake_lamports: u64,
+    pub stake_deposit_unstake_lamports: u64,
+    pub total_unstake_lamports: u64,
+}
+
+#[event]
+#[derive(Debug, Clone)]
+pub struct RebalanceEvent {
+    pub vote_account: Pubkey,
+    pub epoch: u16,
+    pub rebalance_type_tag: RebalanceTypeTag,
+    pub increase_lamports: u64,
+    pub decrease_components: DecreaseComponents,
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
+pub enum RebalanceTypeTag {
+    None,
+    Increase,
+    Decrease,
+}
+
+impl IdlBuild for RebalanceTypeTag {
+    fn create_type() -> Option<IdlTypeDef> {
+        Some(IdlTypeDef {
+            name: "RebalanceTypeTag".to_string(),
+            ty: IdlTypeDefTy::Enum {
+                variants: vec![
+                    IdlEnumVariant {
+                        name: "None".to_string(),
+                        fields: None,
+                    },
+                    IdlEnumVariant {
+                        name: "Increase".to_string(),
+                        fields: None,
+                    },
+                    IdlEnumVariant {
+                        name: "Decrease".to_string(),
+                        fields: None,
+                    },
+                ],
+            },
+            docs: Default::default(),
+            generics: Default::default(),
+            serialization: Default::default(),
+            repr: Default::default(),
+        })
+    }
+}
diff --git a/programs/steward/src/instructions/add_validator_to_blacklist.rs b/programs/steward/src/instructions/add_validator_to_blacklist.rs
index d48774ae..404bee33 100644
--- a/programs/steward/src/instructions/add_validator_to_blacklist.rs
+++ b/programs/steward/src/instructions/add_validator_to_blacklist.rs
@@ -1,4 +1,4 @@
-use crate::{utils::get_config_authority, Config};
+use crate::{utils::get_config_blacklist_authority, Config};
 use anchor_lang::prelude::*;
 
 #[derive(Accounts)]
@@ -6,14 +6,16 @@ pub struct AddValidatorToBlacklist<'info> {
     #[account(mut)]
     pub config: AccountLoader<'info, Config>,
 
-    #[account(mut, address = get_config_authority(&config)?)]
+    #[account(mut, address = get_config_blacklist_authority(&config)?)]
     pub authority: Signer<'info>,
 }
 
 // Removes ability for validator to receive delegation. Score will be set to 0 and instant unstaking will occur.
 // Index is the index of the validator from ValidatorHistory.
-pub fn handler(ctx: Context<AddValidatorToBlacklist>, validator_list_index: u32) -> Result<()> {
+pub fn handler(ctx: Context<AddValidatorToBlacklist>, validator_history_index: u32) -> Result<()> {
     let mut config = ctx.accounts.config.load_mut()?;
-    config.blacklist.set(validator_list_index as usize, true)?;
+    config
+        .validator_history_blacklist
+        .set(validator_history_index as usize, true)?;
     Ok(())
 }
diff --git a/programs/steward/src/instructions/auto_add_validator_to_pool.rs b/programs/steward/src/instructions/auto_add_validator_to_pool.rs
index 8ecb5b47..6fab6031 100644
--- a/programs/steward/src/instructions/auto_add_validator_to_pool.rs
+++ b/programs/steward/src/instructions/auto_add_validator_to_pool.rs
@@ -1,15 +1,27 @@
 use crate::constants::{MAX_VALIDATORS, STAKE_POOL_WITHDRAW_SEED};
 use crate::errors::StewardError;
-use crate::state::{Config, Staker};
-use crate::utils::{deserialize_stake_pool, get_stake_pool_address};
+use crate::events::AutoAddValidatorEvent;
+use crate::state::{Config, StewardStateAccount};
+use crate::utils::{
+    add_validator_check, deserialize_stake_pool, get_stake_pool_address, get_validator_list_length,
+};
 use anchor_lang::prelude::*;
 use anchor_lang::solana_program::{program::invoke_signed, stake, sysvar, vote};
 use spl_stake_pool::find_stake_program_address;
-use spl_stake_pool::state::ValidatorListHeader;
 use validator_history::state::ValidatorHistory;
+use validator_history::ValidatorHistoryEntry;
 
 #[derive(Accounts)]
 pub struct AutoAddValidator<'info> {
+    pub config: AccountLoader<'info, Config>,
+
+    #[account(
+        mut,
+        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
+        bump
+    )]
+    pub steward_state: AccountLoader<'info, StewardStateAccount>,
+
     // Only adding validators where this exists
     #[account(
         seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()],
@@ -18,14 +30,6 @@ pub struct AutoAddValidator<'info> {
     )]
     pub validator_history_account: AccountLoader<'info, ValidatorHistory>,
 
-    pub config: AccountLoader<'info, Config>,
-
-    /// CHECK: CPI address
-    #[account(
-        address = spl_stake_pool::ID
-    )]
-    pub stake_pool_program: AccountInfo<'info>,
-
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(
         mut,
@@ -33,12 +37,6 @@ pub struct AutoAddValidator<'info> {
     )]
     pub stake_pool: AccountInfo<'info>,
 
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
-
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(mut, address = deserialize_stake_pool(&stake_pool)?.reserve_stake)]
     pub reserve_stake: AccountInfo<'info>,
@@ -74,10 +72,6 @@ pub struct AutoAddValidator<'info> {
     #[account(owner = vote::program::ID)]
     pub vote_account: AccountInfo<'info>,
 
-    pub rent: Sysvar<'info, Rent>,
-
-    pub clock: Sysvar<'info, Clock>,
-
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = sysvar::stake_history::ID)]
     pub stake_history: AccountInfo<'info>,
@@ -86,14 +80,21 @@ pub struct AutoAddValidator<'info> {
     #[account(address = stake::config::ID)]
     pub stake_config: AccountInfo<'info>,
 
-    pub system_program: Program<'info, System>,
-
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = stake::program::ID)]
     pub stake_program: AccountInfo<'info>,
 
-    #[account(mut)]
-    pub signer: Signer<'info>,
+    /// CHECK: CPI address
+    #[account(
+        address = spl_stake_pool::ID
+    )]
+    pub stake_pool_program: AccountInfo<'info>,
+
+    pub system_program: Program<'info, System>,
+
+    pub rent: Sysvar<'info, Rent>,
+
+    pub clock: Sysvar<'info, Clock>,
 }
 
 /*
@@ -102,49 +103,58 @@ all the validators we want to be eligible for delegation, as well as to accept s
 Performs some eligibility checks in order to not fill up the validator list with offline or malicious validators.
 */
 pub fn handler(ctx: Context<AutoAddValidator>) -> Result<()> {
-    let config = ctx.accounts.config.load()?;
-    let validator_history = ctx.accounts.validator_history_account.load()?;
-    let epoch = Clock::get()?.epoch;
-
     {
-        let validator_list_data = &mut ctx.accounts.validator_list.try_borrow_mut_data()?;
-        let (_, validator_list) = ValidatorListHeader::deserialize_vec(validator_list_data)?;
+        let mut state_account = ctx.accounts.steward_state.load_mut()?;
+        let config = ctx.accounts.config.load()?;
+        let validator_history = ctx.accounts.validator_history_account.load()?;
+        let validator_list = &ctx.accounts.validator_list;
+        let clock = Clock::get()?;
+        let epoch = clock.epoch;
 
-        if validator_list.len().checked_add(1).unwrap() > MAX_VALIDATORS as u32 {
+        add_validator_check(&clock, &config, &state_account, validator_list)?;
+
+        let validator_list_len = get_validator_list_length(&ctx.accounts.validator_list)?;
+        if validator_list_len.checked_add(1).unwrap() > MAX_VALIDATORS {
             return Err(StewardError::MaxValidatorsReached.into());
         }
-    }
 
-    let start_epoch =
-        epoch.saturating_sub(config.parameters.minimum_voting_epochs.saturating_sub(1));
-    if let Some(entry) = validator_history.history.last() {
-        // Steward requires that validators have been active for last minimum_voting_epochs epochs
-        if validator_history
-            .history
-            .epoch_credits_range(start_epoch as u16, epoch as u16)
-            .iter()
-            .any(|entry| entry.is_none())
-        {
+        let start_epoch =
+            epoch.saturating_sub(config.parameters.minimum_voting_epochs.saturating_sub(1));
+        if let Some(entry) = validator_history.history.last() {
+            // Steward requires that validators have been active for last minimum_voting_epochs epochs
+            if validator_history
+                .history
+                .epoch_credits_range(start_epoch as u16, epoch as u16)
+                .iter()
+                .any(|entry| entry.is_none())
+            {
+                return Err(StewardError::ValidatorBelowLivenessMinimum.into());
+            }
+            if entry.activated_stake_lamports
+                == ValidatorHistoryEntry::default().activated_stake_lamports
+            {
+                return Err(StewardError::StakeHistoryNotRecentEnough.into());
+            }
+            if entry.activated_stake_lamports < config.parameters.minimum_stake_lamports {
+                return Err(StewardError::ValidatorBelowStakeMinimum.into());
+            }
+        } else {
             return Err(StewardError::ValidatorBelowLivenessMinimum.into());
         }
-        if entry.activated_stake_lamports < config.parameters.minimum_stake_lamports {
-            msg!(
-                "Validator {} below minimum. Required: {} Actual: {}",
-                validator_history.vote_account,
-                config.parameters.minimum_stake_lamports,
-                entry.activated_stake_lamports
-            );
-            return Err(StewardError::ValidatorBelowStakeMinimum.into());
-        }
-    } else {
-        return Err(StewardError::ValidatorBelowLivenessMinimum.into());
+
+        state_account.state.increment_validator_to_add()?;
+
+        emit!(AutoAddValidatorEvent {
+            vote_account: ctx.accounts.vote_account.key(),
+            validator_list_index: validator_list_len as u64
+        });
     }
 
     invoke_signed(
         &spl_stake_pool::instruction::add_validator_to_pool(
             &ctx.accounts.stake_pool_program.key(),
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.steward_state.key(),
             &ctx.accounts.reserve_stake.key(),
             &ctx.accounts.withdraw_authority.key(),
             &ctx.accounts.validator_list.key(),
@@ -154,7 +164,7 @@ pub fn handler(ctx: Context<AutoAddValidator>) -> Result<()> {
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.steward_state.to_account_info(),
             ctx.accounts.reserve_stake.to_owned(),
             ctx.accounts.withdraw_authority.to_owned(),
             ctx.accounts.validator_list.to_account_info(),
@@ -168,9 +178,9 @@ pub fn handler(ctx: Context<AutoAddValidator>) -> Result<()> {
             ctx.accounts.stake_program.to_account_info(),
         ],
         &[&[
-            Staker::SEED,
+            StewardStateAccount::SEED,
             &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
+            &[ctx.bumps.steward_state],
         ]],
     )?;
 
diff --git a/programs/steward/src/instructions/auto_remove_validator_from_pool.rs b/programs/steward/src/instructions/auto_remove_validator_from_pool.rs
index 15b8b67d..84e01606 100644
--- a/programs/steward/src/instructions/auto_remove_validator_from_pool.rs
+++ b/programs/steward/src/instructions/auto_remove_validator_from_pool.rs
@@ -2,21 +2,26 @@ use std::num::NonZeroU32;
 
 use crate::constants::STAKE_POOL_WITHDRAW_SEED;
 use crate::errors::StewardError;
-use crate::state::{Config, Staker};
+use crate::events::AutoRemoveValidatorEvent;
+use crate::state::Config;
 use crate::utils::{
     deserialize_stake_pool, get_stake_pool_address, get_validator_stake_info_at_index,
+    remove_validator_check,
 };
 use crate::StewardStateAccount;
-use anchor_lang::solana_program::{program::invoke_signed, stake, sysvar, vote};
-use anchor_lang::{prelude::*, system_program};
+use anchor_lang::prelude::*;
+use anchor_lang::solana_program::{clock::Epoch, program::invoke_signed, stake, sysvar, vote};
 use spl_pod::solana_program::borsh1::try_from_slice_unchecked;
 use spl_pod::solana_program::stake::state::StakeStateV2;
+use spl_stake_pool::state::StakeStatus;
 use spl_stake_pool::{find_stake_program_address, find_transient_stake_program_address};
 use validator_history::state::ValidatorHistory;
 
 #[derive(Accounts)]
 #[instruction(validator_list_index: u64)]
 pub struct AutoRemoveValidator<'info> {
+    pub config: AccountLoader<'info, Config>,
+
     #[account(
         seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()],
         seeds::program = validator_history::ID,
@@ -24,8 +29,6 @@ pub struct AutoRemoveValidator<'info> {
     )]
     pub validator_history_account: AccountLoader<'info, ValidatorHistory>,
 
-    pub config: AccountLoader<'info, Config>,
-
     #[account(
         mut,
         seeds = [StewardStateAccount::SEED, config.key().as_ref()],
@@ -33,12 +36,6 @@ pub struct AutoRemoveValidator<'info> {
     )]
     pub state_account: AccountLoader<'info, StewardStateAccount>,
 
-    /// CHECK: CPI address
-    #[account(
-        address = spl_stake_pool::ID
-    )]
-    pub stake_pool_program: AccountInfo<'info>,
-
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(
         mut,
@@ -46,12 +43,6 @@ pub struct AutoRemoveValidator<'info> {
     )]
     pub stake_pool: AccountInfo<'info>,
 
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
-
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(mut, address = deserialize_stake_pool(&stake_pool)?.reserve_stake)]
     pub reserve_stake: AccountInfo<'info>,
@@ -102,14 +93,9 @@ pub struct AutoRemoveValidator<'info> {
     )]
     pub transient_stake_account: AccountInfo<'info>,
 
-    /// CHECK: passing through, checks are done by spl-stake-pool
-    #[account(constraint = (vote_account.owner == &vote::program::ID ||  vote_account.owner == &system_program::ID))]
+    /// CHECK: Owner check done in handler
     pub vote_account: AccountInfo<'info>,
 
-    pub rent: Sysvar<'info, Rent>,
-
-    pub clock: Sysvar<'info, Clock>,
-
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = sysvar::stake_history::ID)]
     pub stake_history: AccountInfo<'info>,
@@ -118,86 +104,186 @@ pub struct AutoRemoveValidator<'info> {
     #[account(address = stake::config::ID)]
     pub stake_config: AccountInfo<'info>,
 
-    pub system_program: Program<'info, System>,
-
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = stake::program::ID)]
     pub stake_program: AccountInfo<'info>,
 
-    #[account(mut)]
-    pub signer: Signer<'info>,
-}
+    /// CHECK: CPI address
+    #[account(
+        address = spl_stake_pool::ID
+    )]
+    pub stake_pool_program: AccountInfo<'info>,
 
-/*
+    pub system_program: Program<'info, System>,
+
+    pub rent: Sysvar<'info, Rent>,
+
+    pub clock: Sysvar<'info, Clock>,
+}
 
-*/
 pub fn handler(ctx: Context<AutoRemoveValidator>, validator_list_index: usize) -> Result<()> {
-    let mut state_account = ctx.accounts.state_account.load_mut()?;
-    let validator_list = &ctx.accounts.validator_list;
-    let epoch = Clock::get()?.epoch;
-
-    let validator_stake_info =
-        get_validator_stake_info_at_index(validator_list, validator_list_index)?;
-    require!(
-        validator_stake_info.vote_account_address == ctx.accounts.vote_account.key(),
-        StewardError::ValidatorNotInList
-    );
-
-    // Checks state for deactivate delinquent status, preventing pool from merging stake with activating
-    let stake_account_deactivated = {
-        let stake_account_data = &mut ctx.accounts.stake_account.data.borrow_mut();
-        let stake_state: StakeStateV2 =
-            try_from_slice_unchecked::<StakeStateV2>(stake_account_data)?;
-
-        let deactivation_epoch = match stake_state {
-            StakeStateV2::Stake(_meta, stake, _stake_flags) => stake.delegation.deactivation_epoch,
-            _ => return Err(StewardError::InvalidState.into()), // TODO fix
+    let stake_account_deactivated;
+    let vote_account_closed;
+    let clock = Clock::get()?;
+    let epoch = clock.epoch;
+
+    {
+        let config = ctx.accounts.config.load()?;
+        let state_account = ctx.accounts.state_account.load()?;
+        let validator_list = &ctx.accounts.validator_list;
+
+        remove_validator_check(&clock, &config, &state_account, validator_list)?;
+
+        let validator_stake_info =
+            get_validator_stake_info_at_index(validator_list, validator_list_index)?;
+        require!(
+            validator_stake_info.vote_account_address == ctx.accounts.vote_account.key(),
+            StewardError::ValidatorNotInList
+        );
+
+        // Checks state for deactivate delinquent status, preventing pool from merging stake with activating
+        stake_account_deactivated = {
+            let stake_account_data = &mut ctx.accounts.stake_account.data.borrow_mut();
+            let stake_state: StakeStateV2 =
+                try_from_slice_unchecked::<StakeStateV2>(stake_account_data)?;
+
+            let deactivation_epoch = match stake_state {
+                StakeStateV2::Stake(_meta, stake, _stake_flags) => {
+                    stake.delegation.deactivation_epoch
+                }
+                _ => return Err(StewardError::StakeStateIsNotStake.into()),
+            };
+            deactivation_epoch < epoch
         };
-        deactivation_epoch < epoch
-    };
-
-    // Check if vote account closed
-    let vote_account_closed = ctx.accounts.vote_account.owner == &system_program::ID;
-
-    require!(
-        stake_account_deactivated || vote_account_closed,
-        StewardError::ValidatorNotRemovable
-    );
-
-    state_account.state.remove_validator(validator_list_index)?;
-
-    invoke_signed(
-        &spl_stake_pool::instruction::remove_validator_from_pool(
-            &ctx.accounts.stake_pool_program.key(),
-            &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
-            &ctx.accounts.withdraw_authority.key(),
-            &ctx.accounts.validator_list.key(),
-            &ctx.accounts.stake_account.key(),
-            &ctx.accounts.transient_stake_account.key(),
-        ),
-        &[
-            ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
-            ctx.accounts.reserve_stake.to_owned(),
-            ctx.accounts.withdraw_authority.to_owned(),
-            ctx.accounts.validator_list.to_account_info(),
-            ctx.accounts.stake_account.to_account_info(),
-            ctx.accounts.transient_stake_account.to_account_info(),
-            ctx.accounts.vote_account.to_account_info(),
-            ctx.accounts.rent.to_account_info(),
-            ctx.accounts.clock.to_account_info(),
-            ctx.accounts.stake_history.to_account_info(),
-            ctx.accounts.stake_config.to_account_info(),
-            ctx.accounts.system_program.to_account_info(),
-            ctx.accounts.stake_program.to_account_info(),
-        ],
-        &[&[
-            Staker::SEED,
-            &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
-        ]],
-    )?;
+
+        // Check if vote account closed
+        vote_account_closed = *ctx.accounts.vote_account.owner != vote::program::ID;
+
+        require!(
+            stake_account_deactivated || vote_account_closed,
+            StewardError::ValidatorNotRemovable
+        );
+    }
+
+    {
+        invoke_signed(
+            &spl_stake_pool::instruction::remove_validator_from_pool(
+                &ctx.accounts.stake_pool_program.key(),
+                &ctx.accounts.stake_pool.key(),
+                &ctx.accounts.state_account.key(),
+                &ctx.accounts.withdraw_authority.key(),
+                &ctx.accounts.validator_list.key(),
+                &ctx.accounts.stake_account.key(),
+                &ctx.accounts.transient_stake_account.key(),
+            ),
+            &[
+                ctx.accounts.stake_pool.to_account_info(),
+                ctx.accounts.state_account.to_account_info(),
+                ctx.accounts.reserve_stake.to_owned(),
+                ctx.accounts.withdraw_authority.to_owned(),
+                ctx.accounts.validator_list.to_account_info(),
+                ctx.accounts.stake_account.to_account_info(),
+                ctx.accounts.transient_stake_account.to_account_info(),
+                ctx.accounts.vote_account.to_account_info(),
+                ctx.accounts.rent.to_account_info(),
+                ctx.accounts.clock.to_account_info(),
+                ctx.accounts.stake_history.to_account_info(),
+                ctx.accounts.stake_config.to_account_info(),
+                ctx.accounts.system_program.to_account_info(),
+                ctx.accounts.stake_program.to_account_info(),
+            ],
+            &[&[
+                StewardStateAccount::SEED,
+                &ctx.accounts.config.key().to_bytes(),
+                &[ctx.bumps.state_account],
+            ]],
+        )?;
+    }
+
+    {
+        // Read the state account again
+        let mut state_account = ctx.accounts.state_account.load_mut()?;
+        let validator_list = &ctx.accounts.validator_list;
+        let validator_stake_info =
+            get_validator_stake_info_at_index(validator_list, validator_list_index)?;
+
+        let stake_status = StakeStatus::try_from(validator_stake_info.status)?;
+        let marked_for_immediate_removal: bool;
+
+        let stake_pool = deserialize_stake_pool(&ctx.accounts.stake_pool)?;
+
+        match stake_status {
+            StakeStatus::Active => {
+                // Should never happen
+                return Err(StewardError::ValidatorMarkedActive.into());
+            }
+            StakeStatus::DeactivatingValidator => {
+                let stake_account_data = &mut ctx.accounts.stake_account.data.borrow_mut();
+                let (meta, stake) =
+                    match try_from_slice_unchecked::<StakeStateV2>(stake_account_data)? {
+                        StakeStateV2::Stake(meta, stake, _stake_flags) => (meta, stake),
+                        _ => return Err(StewardError::StakeStateIsNotStake.into()),
+                    };
+
+                if stake_is_usable_by_pool(
+                    &meta,
+                    ctx.accounts.withdraw_authority.key,
+                    &stake_pool.lockup,
+                ) && stake_is_inactive_without_history(&stake, epoch)
+                {
+                    state_account
+                        .state
+                        .mark_validator_for_immediate_removal(validator_list_index)?;
+                    marked_for_immediate_removal = true;
+                } else {
+                    state_account
+                        .state
+                        .mark_validator_for_removal(validator_list_index)?;
+                    marked_for_immediate_removal = false;
+                }
+            }
+            StakeStatus::ReadyForRemoval => {
+                marked_for_immediate_removal = true;
+                state_account
+                    .state
+                    .mark_validator_for_immediate_removal(validator_list_index)?;
+            }
+            StakeStatus::DeactivatingAll | StakeStatus::DeactivatingTransient => {
+                marked_for_immediate_removal = false;
+                state_account
+                    .state
+                    .mark_validator_for_removal(validator_list_index)?;
+            }
+        }
+
+        emit!(AutoRemoveValidatorEvent {
+            vote_account: ctx.accounts.vote_account.key(),
+            validator_list_index: validator_list_index as u64,
+            stake_account_deactivated,
+            vote_account_closed,
+            marked_for_immediate_removal,
+        });
+    }
 
     Ok(())
 }
+
+// CHECKS FROM spl_stake_pool::processor::update_validator_list_balance
+
+/// Checks if a stake account can be managed by the pool
+fn stake_is_usable_by_pool(
+    meta: &stake::state::Meta,
+    expected_authority: &Pubkey,
+    expected_lockup: &stake::state::Lockup,
+) -> bool {
+    meta.authorized.staker == *expected_authority
+        && meta.authorized.withdrawer == *expected_authority
+        && meta.lockup == *expected_lockup
+}
+
+/// Checks if a stake account is active, without taking into account cooldowns
+fn stake_is_inactive_without_history(stake: &stake::state::Stake, epoch: Epoch) -> bool {
+    stake.delegation.deactivation_epoch < epoch
+        || (stake.delegation.activation_epoch == epoch
+            && stake.delegation.deactivation_epoch == epoch)
+}
diff --git a/programs/steward/src/instructions/close_steward_accounts.rs b/programs/steward/src/instructions/close_steward_accounts.rs
new file mode 100644
index 00000000..7c561bb8
--- /dev/null
+++ b/programs/steward/src/instructions/close_steward_accounts.rs
@@ -0,0 +1,30 @@
+use crate::{
+    state::{Config, StewardStateAccount},
+    utils::get_config_admin,
+};
+use anchor_lang::prelude::*;
+
+#[derive(Accounts)]
+pub struct CloseStewardAccounts<'info> {
+    pub config: AccountLoader<'info, Config>,
+
+    #[account(
+        mut,
+        close = authority,
+        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
+        bump
+    )]
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
+
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub authority: Signer<'info>,
+}
+
+/*
+    Closes Steward PDA accounts associated with a given Config (StewardStateAccount, and Staker).
+    Config is not closed as it is a Keypair, so lamports can simply be withdrawn.
+    Reclaims lamports to authority
+*/
+pub const fn handler(_ctx: Context<CloseStewardAccounts>) -> Result<()> {
+    Ok(())
+}
diff --git a/programs/steward/src/instructions/compute_delegations.rs b/programs/steward/src/instructions/compute_delegations.rs
index 679144df..694f1ca0 100644
--- a/programs/steward/src/instructions/compute_delegations.rs
+++ b/programs/steward/src/instructions/compute_delegations.rs
@@ -1,5 +1,5 @@
-use crate::errors::StewardError;
-use crate::{maybe_transition_and_emit, Config, StewardStateAccount};
+use crate::utils::{get_validator_list, state_checks};
+use crate::{maybe_transition, Config, StewardStateAccount, StewardStateEnum};
 use anchor_lang::prelude::*;
 
 #[derive(Accounts)]
@@ -13,8 +13,9 @@ pub struct ComputeDelegations<'info> {
     )]
     pub state_account: AccountLoader<'info, StewardStateAccount>,
 
-    #[account(mut)]
-    pub signer: Signer<'info>,
+    /// CHECK: Account owner checked, account type checked in get_validator_stake_info_at_index
+    #[account(address = get_validator_list(&config)?)]
+    pub validator_list: AccountInfo<'info>,
 }
 
 /*
@@ -24,24 +25,29 @@ It computes a share of the pool for each validator.
 pub fn handler(ctx: Context<ComputeDelegations>) -> Result<()> {
     let config = ctx.accounts.config.load()?;
     let mut state_account = ctx.accounts.state_account.load_mut()?;
-
     let clock = Clock::get()?;
     let epoch_schedule = EpochSchedule::get()?;
 
-    if config.is_paused() {
-        return Err(StewardError::StateMachinePaused.into());
-    }
+    state_checks(
+        &clock,
+        &config,
+        &state_account,
+        &ctx.accounts.validator_list,
+        Some(StewardStateEnum::ComputeDelegations),
+    )?;
 
     state_account
         .state
         .compute_delegations(clock.epoch, &config)?;
 
-    maybe_transition_and_emit(
+    if let Some(event) = maybe_transition(
         &mut state_account.state,
         &clock,
         &config.parameters,
         &epoch_schedule,
-    )?;
+    )? {
+        emit!(event);
+    }
 
     Ok(())
 }
diff --git a/programs/steward/src/instructions/compute_instant_unstake.rs b/programs/steward/src/instructions/compute_instant_unstake.rs
index 9bcd64f4..367f57f4 100644
--- a/programs/steward/src/instructions/compute_instant_unstake.rs
+++ b/programs/steward/src/instructions/compute_instant_unstake.rs
@@ -1,6 +1,8 @@
 use crate::{
-    errors::StewardError, maybe_transition_and_emit, utils::get_validator_stake_info_at_index,
-    Config, StewardStateAccount,
+    errors::StewardError,
+    maybe_transition,
+    utils::{get_validator_list, get_validator_stake_info_at_index, state_checks},
+    Config, StewardStateAccount, StewardStateEnum,
 };
 use anchor_lang::prelude::*;
 use validator_history::{ClusterHistory, ValidatorHistory};
@@ -16,10 +18,11 @@ pub struct ComputeInstantUnstake<'info> {
     )]
     pub state_account: AccountLoader<'info, StewardStateAccount>,
 
+    /// CHECK: We check it is the correct vote account in the handler
     pub validator_history: AccountLoader<'info, ValidatorHistory>,
 
-    /// CHECK: TODO add validator list to config
-    #[account(owner = spl_stake_pool::id())]
+    #[account(address = get_validator_list(&config)?)]
+    /// CHECK: We check against the Config
     pub validator_list: AccountInfo<'info>,
 
     #[account(
@@ -28,9 +31,6 @@ pub struct ComputeInstantUnstake<'info> {
         bump
     )]
     pub cluster_history: AccountLoader<'info, ClusterHistory>,
-
-    #[account(mut)]
-    pub signer: Signer<'info>,
 }
 
 pub fn handler(ctx: Context<ComputeInstantUnstake>, validator_list_index: usize) -> Result<()> {
@@ -42,6 +42,14 @@ pub fn handler(ctx: Context<ComputeInstantUnstake>, validator_list_index: usize)
     let clock = Clock::get()?;
     let epoch_schedule = EpochSchedule::get()?;
 
+    state_checks(
+        &clock,
+        &config,
+        &state_account,
+        &ctx.accounts.validator_list,
+        Some(StewardStateEnum::ComputeInstantUnstake),
+    )?;
+
     let validator_stake_info =
         get_validator_stake_info_at_index(validator_list, validator_list_index)?;
     require!(
@@ -49,24 +57,25 @@ pub fn handler(ctx: Context<ComputeInstantUnstake>, validator_list_index: usize)
         StewardError::ValidatorNotInList
     );
 
-    if config.is_paused() {
-        return Err(StewardError::StateMachinePaused.into());
-    }
-
-    state_account.state.compute_instant_unstake(
+    if let Some(instant_unstake) = state_account.state.compute_instant_unstake(
         &clock,
         &epoch_schedule,
         &validator_history,
         validator_list_index,
         &cluster,
         &config,
-    )?;
-    maybe_transition_and_emit(
+    )? {
+        emit!(instant_unstake);
+    }
+
+    if let Some(event) = maybe_transition(
         &mut state_account.state,
         &clock,
         &config.parameters,
         &epoch_schedule,
-    )?;
+    )? {
+        emit!(event);
+    }
 
     Ok(())
 }
diff --git a/programs/steward/src/instructions/compute_score.rs b/programs/steward/src/instructions/compute_score.rs
index 81125003..580029e9 100644
--- a/programs/steward/src/instructions/compute_score.rs
+++ b/programs/steward/src/instructions/compute_score.rs
@@ -1,8 +1,12 @@
 use anchor_lang::prelude::*;
-use spl_stake_pool::state::ValidatorListHeader;
 
 use crate::{
-    errors::StewardError, maybe_transition_and_emit, utils::get_validator_stake_info_at_index,
+    errors::StewardError,
+    maybe_transition,
+    utils::{
+        get_validator_list, get_validator_list_length, get_validator_stake_info_at_index,
+        state_checks,
+    },
     Config, StewardStateAccount, StewardStateEnum,
 };
 use validator_history::{ClusterHistory, ValidatorHistory};
@@ -21,7 +25,7 @@ pub struct ComputeScore<'info> {
     pub validator_history: AccountLoader<'info, ValidatorHistory>,
 
     /// CHECK: Account owner checked, account type checked in get_validator_stake_info_at_index
-    #[account(owner = spl_stake_pool::id())]
+    #[account(address = get_validator_list(&config)?)]
     pub validator_list: AccountInfo<'info>,
 
     #[account(
@@ -30,9 +34,6 @@ pub struct ComputeScore<'info> {
         bump
     )]
     pub cluster_history: AccountLoader<'info, ClusterHistory>,
-
-    #[account(mut)]
-    pub signer: Signer<'info>,
 }
 
 pub fn handler(ctx: Context<ComputeScore>, validator_list_index: usize) -> Result<()> {
@@ -44,6 +45,9 @@ pub fn handler(ctx: Context<ComputeScore>, validator_list_index: usize) -> Resul
     let clock: Clock = Clock::get()?;
     let epoch_schedule = EpochSchedule::get()?;
 
+    // We don't check the state here because we force it below
+    state_checks(&clock, &config, &state_account, validator_list, None)?;
+
     let validator_stake_info =
         get_validator_stake_info_at_index(validator_list, validator_list_index)?;
     require!(
@@ -51,29 +55,22 @@ pub fn handler(ctx: Context<ComputeScore>, validator_list_index: usize) -> Resul
         StewardError::ValidatorNotInList
     );
 
-    let num_pool_validators = {
-        let mut validator_list_data = validator_list.try_borrow_mut_data()?;
-        let (_, validator_list) = ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
-        validator_list.len() as u64
-    };
-
-    if config.is_paused() {
-        return Err(StewardError::StateMachinePaused.into());
-    }
-
     // May need to force an extra transition here in case cranking got stuck in any previous state
     // and it's now the start of a new scoring cycle
     if !matches!(
         state_account.state.state_tag,
         StewardStateEnum::ComputeScores
     ) {
-        maybe_transition_and_emit(
+        if let Some(event) = maybe_transition(
             &mut state_account.state,
             &clock,
             &config.parameters,
             &epoch_schedule,
-        )?;
+        )? {
+            emit!(event);
+        }
     }
+
     require!(
         matches!(
             state_account.state.state_tag,
@@ -82,22 +79,28 @@ pub fn handler(ctx: Context<ComputeScore>, validator_list_index: usize) -> Resul
         StewardError::InvalidState
     );
 
-    state_account.state.compute_score(
+    let num_pool_validators = get_validator_list_length(validator_list)?;
+
+    if let Some(score) = state_account.state.compute_score(
         &clock,
         &epoch_schedule,
         &validator_history,
         validator_list_index,
         &cluster_history,
         &config,
-        num_pool_validators,
-    )?;
+        num_pool_validators as u64,
+    )? {
+        emit!(score);
+    }
 
-    maybe_transition_and_emit(
+    if let Some(event) = maybe_transition(
         &mut state_account.state,
         &clock,
         &config.parameters,
         &epoch_schedule,
-    )?;
+    )? {
+        emit!(event);
+    }
 
     Ok(())
 }
diff --git a/programs/steward/src/instructions/epoch_maintenance.rs b/programs/steward/src/instructions/epoch_maintenance.rs
new file mode 100644
index 00000000..2b3cafe7
--- /dev/null
+++ b/programs/steward/src/instructions/epoch_maintenance.rs
@@ -0,0 +1,122 @@
+use crate::{
+    errors::StewardError,
+    events::EpochMaintenanceEvent,
+    utils::{
+        check_validator_list_has_stake_status_other_than, deserialize_stake_pool,
+        get_stake_pool_address, get_validator_list, get_validator_list_length,
+    },
+    Config, StewardStateAccount, COMPUTE_INSTANT_UNSTAKES, EPOCH_MAINTENANCE, POST_LOOP_IDLE,
+    PRE_LOOP_IDLE, REBALANCE, RESET_TO_IDLE,
+};
+use anchor_lang::prelude::*;
+use spl_stake_pool::state::StakeStatus;
+
+#[derive(Accounts)]
+pub struct EpochMaintenance<'info> {
+    pub config: AccountLoader<'info, Config>,
+
+    #[account(
+        mut,
+        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
+        bump
+    )]
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
+
+    /// CHECK: Correct account guaranteed if address is correct
+    #[account(address = get_validator_list(&config)?)]
+    pub validator_list: AccountInfo<'info>,
+
+    /// CHECK: Correct account guaranteed if address is correct
+    #[account(
+        address = get_stake_pool_address(&config)?
+    )]
+    pub stake_pool: AccountInfo<'info>,
+}
+
+/// Runs maintenance tasks at the start of each epoch, needs to be run multiple times
+/// Routines:
+/// - Remove delinquent validators
+pub fn handler(
+    ctx: Context<EpochMaintenance>,
+    validator_index_to_remove: Option<usize>,
+) -> Result<()> {
+    let stake_pool = deserialize_stake_pool(&ctx.accounts.stake_pool)?;
+    let mut state_account = ctx.accounts.state_account.load_mut()?;
+
+    let clock = Clock::get()?;
+
+    require!(
+        clock.epoch == stake_pool.last_update_epoch,
+        StewardError::StakePoolNotUpdated
+    );
+
+    require!(
+        state_account.state.current_epoch < clock.epoch,
+        StewardError::EpochMaintenanceAlreadyComplete
+    );
+
+    // Ensure there are no validators in the list that have not been removed, that should be
+    require!(
+        !check_validator_list_has_stake_status_other_than(
+            &ctx.accounts.validator_list,
+            &[StakeStatus::Active]
+        )?,
+        StewardError::ValidatorsHaveNotBeenRemoved
+    );
+
+    state_account.state.unset_flag(EPOCH_MAINTENANCE);
+
+    {
+        // Routine - Remove marked validators
+        // We still want these checks to run even if we don't specify a validator to remove
+        let validators_in_list = get_validator_list_length(&ctx.accounts.validator_list)?;
+        let validators_to_remove = state_account.state.validators_to_remove.count()
+            + state_account.state.validators_for_immediate_removal.count();
+
+        // Ensure we have a 1-1 mapping between the number of validators in the list and the number of validators in the state
+        // If we don't have this mapping, everything needs to be removed
+        require!(
+            state_account.state.num_pool_validators as usize
+                + state_account.state.validators_added as usize
+                - validators_to_remove
+                == validators_in_list,
+            StewardError::ListStateMismatch
+        );
+        if let Some(validator_index_to_remove) = validator_index_to_remove {
+            state_account
+                .state
+                .remove_validator(validator_index_to_remove)?;
+        }
+    }
+
+    {
+        // Routine - Update state
+        let okay_to_update = state_account.state.validators_to_remove.is_empty()
+            && state_account
+                .state
+                .validators_for_immediate_removal
+                .is_empty();
+
+        if okay_to_update {
+            state_account.state.current_epoch = clock.epoch;
+
+            // We keep Compute Scores and Compute Delegations to be unset on next epoch cycle
+            state_account
+                .state
+                .unset_flag(PRE_LOOP_IDLE | COMPUTE_INSTANT_UNSTAKES | REBALANCE | POST_LOOP_IDLE);
+            state_account
+                .state
+                .set_flag(RESET_TO_IDLE | EPOCH_MAINTENANCE);
+        }
+        emit!(EpochMaintenanceEvent {
+            validator_index_to_remove: validator_index_to_remove.map(|x| x as u64),
+            validator_list_length: get_validator_list_length(&ctx.accounts.validator_list)? as u64,
+            num_pool_validators: state_account.state.num_pool_validators,
+            validators_to_remove: state_account.state.validators_to_remove.count() as u64,
+            validators_to_add: state_account.state.validators_added as u64,
+            maintenance_complete: okay_to_update,
+        });
+    }
+
+    Ok(())
+}
diff --git a/programs/steward/src/instructions/idle.rs b/programs/steward/src/instructions/idle.rs
index 4d647612..4afc6585 100644
--- a/programs/steward/src/instructions/idle.rs
+++ b/programs/steward/src/instructions/idle.rs
@@ -1,7 +1,9 @@
 use anchor_lang::prelude::*;
 
 use crate::{
-    errors::StewardError, maybe_transition_and_emit, Config, StewardStateAccount, StewardStateEnum,
+    maybe_transition,
+    utils::{get_validator_list, state_checks},
+    Config, StewardStateAccount, StewardStateEnum,
 };
 
 #[derive(Accounts)]
@@ -15,8 +17,9 @@ pub struct Idle<'info> {
     )]
     pub state_account: AccountLoader<'info, StewardStateAccount>,
 
-    #[account(mut)]
-    pub signer: Signer<'info>,
+    /// CHECK: account type checked in state_checks and address set in config
+    #[account(address = get_validator_list(&config)?)]
+    pub validator_list: AccountInfo<'info>,
 }
 
 /*
@@ -28,21 +31,22 @@ pub fn handler(ctx: Context<Idle>) -> Result<()> {
     let clock = Clock::get()?;
     let epoch_schedule = EpochSchedule::get()?;
 
-    require!(
-        matches!(state_account.state.state_tag, StewardStateEnum::Idle),
-        StewardError::InvalidState
-    );
-
-    if config.is_paused() {
-        return Err(StewardError::StateMachinePaused.into());
-    }
+    state_checks(
+        &clock,
+        &config,
+        &state_account,
+        &ctx.accounts.validator_list,
+        Some(StewardStateEnum::Idle),
+    )?;
 
-    maybe_transition_and_emit(
+    if let Some(event) = maybe_transition(
         &mut state_account.state,
         &clock,
         &config.parameters,
         &epoch_schedule,
-    )?;
+    )? {
+        emit!(event);
+    }
 
     Ok(())
 }
diff --git a/programs/steward/src/instructions/initialize_state.rs b/programs/steward/src/instructions/initialize_state.rs
deleted file mode 100644
index d637ba84..00000000
--- a/programs/steward/src/instructions/initialize_state.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use crate::{
-    constants::MAX_ALLOC_BYTES,
-    state::{Config, StewardStateAccount},
-};
-use anchor_lang::prelude::*;
-
-#[derive(Accounts)]
-pub struct InitializeState<'info> {
-    #[account(
-        init,
-        payer = signer,
-        space = MAX_ALLOC_BYTES,
-        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
-        bump
-    )]
-    pub state_account: AccountLoader<'info, StewardStateAccount>,
-
-    pub config: AccountLoader<'info, Config>,
-
-    pub system_program: Program<'info, System>,
-
-    #[account(mut)]
-    pub signer: Signer<'info>,
-}
-
-/*
-Initializes steward state account, without assigning any values until it has been reallocated to desired size.
-Split into multiple instructions due to 10240 byte allocation limit for PDAs.
-*/
-pub const fn handler(_ctx: Context<InitializeState>) -> Result<()> {
-    Ok(())
-}
diff --git a/programs/steward/src/instructions/initialize_config.rs b/programs/steward/src/instructions/initialize_steward.rs
similarity index 57%
rename from programs/steward/src/instructions/initialize_config.rs
rename to programs/steward/src/instructions/initialize_steward.rs
index 12a5ccaa..7c146a0f 100644
--- a/programs/steward/src/instructions/initialize_config.rs
+++ b/programs/steward/src/instructions/initialize_steward.rs
@@ -1,27 +1,27 @@
 use anchor_lang::{prelude::*, solana_program::program::invoke};
 
-use crate::{utils::deserialize_stake_pool, Config, Staker, UpdateParametersArgs};
+use crate::{
+    constants::MAX_ALLOC_BYTES, utils::deserialize_stake_pool, Config, StewardStateAccount,
+    UpdateParametersArgs,
+};
 
 #[derive(Accounts)]
-pub struct InitializeConfig<'info> {
+pub struct InitializeSteward<'info> {
     #[account(
         init,
-        payer = signer,
+        payer = current_staker,
         space = Config::SIZE,
     )]
     pub config: AccountLoader<'info, Config>,
 
-    // Creates an account that will be used to sign instructions for the stake pool.
-    // The pool's "staker" keypair needs to be assigned to this address, and it has authority over
-    // adding validators, removing validators, and delegating stake to validators in the pool.
     #[account(
         init,
-        seeds = [Staker::SEED, config.key().as_ref()],
-        payer = signer,
-        space = Staker::SIZE,
+        payer = current_staker,
+        space = MAX_ALLOC_BYTES,
+        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
         bump
     )]
-    pub staker: Account<'info, Staker>,
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
 
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(mut)]
@@ -37,19 +37,26 @@ pub struct InitializeConfig<'info> {
         mut,
         address = deserialize_stake_pool(&stake_pool)?.staker
     )]
-    pub signer: Signer<'info>,
+    pub current_staker: Signer<'info>,
 }
 
 pub fn handler(
-    ctx: Context<InitializeConfig>,
-    authority: Pubkey,
+    ctx: Context<InitializeSteward>,
     update_parameters_args: &UpdateParametersArgs,
 ) -> Result<()> {
     // Confirm that the stake pool is valid
-    let _ = deserialize_stake_pool(&ctx.accounts.stake_pool)?;
+    let stake_pool_account = deserialize_stake_pool(&ctx.accounts.stake_pool)?;
     let mut config = ctx.accounts.config.load_init()?;
+
+    // Set the stake pool information
     config.stake_pool = ctx.accounts.stake_pool.key();
-    config.authority = authority;
+    config.validator_list = stake_pool_account.validator_list;
+
+    // Set all authorities to the current_staker
+    let admin = ctx.accounts.current_staker.key();
+    config.admin = admin;
+    config.blacklist_authority = admin;
+    config.parameters_authority = admin;
 
     // Set Initial Parameters
     let max_slots_in_epoch = EpochSchedule::get()?.slots_per_epoch;
@@ -63,19 +70,18 @@ pub fn handler(
 
     config.parameters = initial_parameters;
 
-    // Set the staker account
-    ctx.accounts.staker.bump = ctx.bumps.staker;
+    // The staker is the state account
     invoke(
         &spl_stake_pool::instruction::set_staker(
             &ctx.accounts.stake_pool_program.key(),
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.signer.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.current_staker.key(),
+            &ctx.accounts.state_account.key(),
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.signer.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.current_staker.to_account_info(),
+            ctx.accounts.state_account.to_account_info(),
         ],
     )?;
     Ok(())
diff --git a/programs/steward/src/instructions/instant_remove_validator.rs b/programs/steward/src/instructions/instant_remove_validator.rs
new file mode 100644
index 00000000..099e87aa
--- /dev/null
+++ b/programs/steward/src/instructions/instant_remove_validator.rs
@@ -0,0 +1,90 @@
+use crate::{
+    errors::StewardError,
+    utils::{
+        check_validator_list_has_stake_status_other_than, deserialize_stake_pool,
+        get_stake_pool_address, get_validator_list, get_validator_list_length,
+    },
+    Config, StewardStateAccount,
+};
+use anchor_lang::prelude::*;
+use spl_stake_pool::state::StakeStatus;
+
+#[derive(Accounts)]
+pub struct InstantRemoveValidator<'info> {
+    pub config: AccountLoader<'info, Config>,
+
+    #[account(
+        mut,
+        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
+        bump
+    )]
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
+
+    /// CHECK: Correct account guaranteed if address is correct
+    #[account(address = get_validator_list(&config)?)]
+    pub validator_list: AccountInfo<'info>,
+
+    /// CHECK: Correct account guaranteed if address is correct
+    #[account(
+        address = get_stake_pool_address(&config)?
+    )]
+    pub stake_pool: AccountInfo<'info>,
+}
+
+/// Removes validators from the pool that have been marked for immediate removal
+pub fn handler(
+    ctx: Context<InstantRemoveValidator>,
+    validator_index_to_remove: usize,
+) -> Result<()> {
+    let stake_pool = deserialize_stake_pool(&ctx.accounts.stake_pool)?;
+    let mut state_account = ctx.accounts.state_account.load_mut()?;
+
+    let clock = Clock::get()?;
+    let validators_to_remove = state_account.state.validators_for_immediate_removal.count();
+    let validators_in_list = get_validator_list_length(&ctx.accounts.validator_list)?;
+
+    require!(
+        state_account.state.current_epoch == clock.epoch,
+        StewardError::EpochMaintenanceNotComplete
+    );
+
+    require!(
+        clock.epoch == stake_pool.last_update_epoch,
+        StewardError::StakePoolNotUpdated
+    );
+
+    require!(
+        state_account
+            .state
+            .validators_for_immediate_removal
+            .get(validator_index_to_remove)?,
+        StewardError::ValidatorNotInList
+    );
+
+    // Ensure there are no validators in the list that have not been removed, that should be
+    require!(
+        !check_validator_list_has_stake_status_other_than(
+            &ctx.accounts.validator_list,
+            &[
+                StakeStatus::Active,
+                StakeStatus::DeactivatingAll,
+                StakeStatus::DeactivatingTransient
+            ]
+        )?,
+        StewardError::ValidatorsHaveNotBeenRemoved
+    );
+
+    require!(
+        state_account.state.num_pool_validators as usize
+            + state_account.state.validators_added as usize
+            - validators_to_remove
+            == validators_in_list,
+        StewardError::ListStateMismatch
+    );
+
+    state_account
+        .state
+        .remove_validator(validator_index_to_remove)?;
+
+    Ok(())
+}
diff --git a/programs/steward/src/instructions/mod.rs b/programs/steward/src/instructions/mod.rs
index 4ca9674e..27337283 100644
--- a/programs/steward/src/instructions/mod.rs
+++ b/programs/steward/src/instructions/mod.rs
@@ -2,16 +2,19 @@
 pub mod add_validator_to_blacklist;
 pub mod auto_add_validator_to_pool;
 pub mod auto_remove_validator_from_pool;
+pub mod close_steward_accounts;
 pub mod compute_delegations;
 pub mod compute_instant_unstake;
 pub mod compute_score;
+pub mod epoch_maintenance;
 pub mod idle;
-pub mod initialize_config;
-pub mod initialize_state;
+pub mod initialize_steward;
+pub mod instant_remove_validator;
 pub mod pause_steward;
 pub mod realloc_state;
 pub mod rebalance;
 pub mod remove_validator_from_blacklist;
+pub mod reset_steward_state;
 pub mod resume_steward;
 pub mod set_new_authority;
 pub mod spl_passthrough;
@@ -20,16 +23,19 @@ pub mod update_parameters;
 pub use add_validator_to_blacklist::*;
 pub use auto_add_validator_to_pool::*;
 pub use auto_remove_validator_from_pool::*;
+pub use close_steward_accounts::*;
 pub use compute_delegations::*;
 pub use compute_instant_unstake::*;
 pub use compute_score::*;
+pub use epoch_maintenance::*;
 pub use idle::*;
-pub use initialize_config::*;
-pub use initialize_state::*;
+pub use initialize_steward::*;
+pub use instant_remove_validator::*;
 pub use pause_steward::*;
 pub use realloc_state::*;
 pub use rebalance::*;
 pub use remove_validator_from_blacklist::*;
+pub use reset_steward_state::*;
 pub use resume_steward::*;
 pub use set_new_authority::*;
 pub use spl_passthrough::*;
diff --git a/programs/steward/src/instructions/pause_steward.rs b/programs/steward/src/instructions/pause_steward.rs
index 9d7f18de..94bc0ee7 100644
--- a/programs/steward/src/instructions/pause_steward.rs
+++ b/programs/steward/src/instructions/pause_steward.rs
@@ -1,13 +1,13 @@
 use anchor_lang::prelude::*;
 
-use crate::{utils::get_config_authority, Config};
+use crate::{utils::get_config_admin, Config};
 
 #[derive(Accounts)]
 pub struct PauseSteward<'info> {
     #[account(mut)]
     pub config: AccountLoader<'info, Config>,
 
-    #[account(mut, address = get_config_authority(&config)?)]
+    #[account(mut, address = get_config_admin(&config)?)]
     pub authority: Signer<'info>,
 }
 
diff --git a/programs/steward/src/instructions/realloc_state.rs b/programs/steward/src/instructions/realloc_state.rs
index fadaf40c..ef9adef3 100644
--- a/programs/steward/src/instructions/realloc_state.rs
+++ b/programs/steward/src/instructions/realloc_state.rs
@@ -3,7 +3,8 @@ use crate::{
     constants::{MAX_ALLOC_BYTES, MAX_VALIDATORS, SORTED_INDEX_DEFAULT},
     errors::StewardError,
     state::{Config, StewardStateAccount},
-    Delegation, StewardStateEnum,
+    utils::get_validator_list,
+    Delegation, StewardStateEnum, STATE_PADDING_0_SIZE,
 };
 use anchor_lang::prelude::*;
 use spl_stake_pool::state::ValidatorListHeader;
@@ -43,10 +44,8 @@ pub struct ReallocState<'info> {
 
     pub config: AccountLoader<'info, Config>,
 
-    /// CHECK: TODO add validator_list address to config
-    #[account(
-        owner = spl_stake_pool::ID,
-    )]
+    /// CHECK: We check against the Config
+    #[account(address = get_validator_list(&config)?)]
     pub validator_list: AccountInfo<'info>,
 
     pub system_program: Program<'info, System>,
@@ -80,16 +79,19 @@ pub fn handler(ctx: Context<ReallocState>) -> Result<()> {
         state_account.state.yield_scores = [0; MAX_VALIDATORS];
         state_account.state.sorted_yield_score_indices = [SORTED_INDEX_DEFAULT; MAX_VALIDATORS];
         state_account.state.progress = BitMask::default();
-        state_account.state.current_epoch = clock.epoch;
+        state_account.state.current_epoch = 0; // will be set by epoch_maintenance
         state_account.state.next_cycle_epoch = clock
             .epoch
             .checked_add(config.parameters.num_epochs_between_scoring)
             .ok_or(StewardError::ArithmeticError)?;
         state_account.state.delegations = [Delegation::default(); MAX_VALIDATORS];
-        state_account.state.rebalance_completed = false.into();
         state_account.state.instant_unstake = BitMask::default();
         state_account.state.start_computing_scores_slot = clock.slot;
-        state_account.state._padding0 = [0; 6 + MAX_VALIDATORS * 8];
+        state_account.state.validators_to_remove = BitMask::default();
+        state_account.state.validators_for_immediate_removal = BitMask::default();
+        state_account.state.validators_added = 0;
+        state_account.state.clear_flags();
+        state_account.state._padding0 = [0; STATE_PADDING_0_SIZE];
     }
 
     Ok(())
diff --git a/programs/steward/src/instructions/rebalance.rs b/programs/steward/src/instructions/rebalance.rs
index 92ef5a46..76d14ca1 100644
--- a/programs/steward/src/instructions/rebalance.rs
+++ b/programs/steward/src/instructions/rebalance.rs
@@ -1,6 +1,7 @@
 use std::num::NonZeroU32;
 
 use anchor_lang::{
+    idl::*,
     prelude::*,
     solana_program::{
         program::invoke_signed,
@@ -8,6 +9,7 @@ use anchor_lang::{
         system_program, sysvar, vote,
     },
 };
+use borsh::BorshDeserialize;
 use spl_pod::solana_program::stake::state::StakeStateV2;
 use spl_stake_pool::{
     find_stake_program_address, find_transient_stake_program_address, minimum_delegation,
@@ -19,9 +21,13 @@ use crate::{
     constants::STAKE_POOL_WITHDRAW_SEED,
     delegation::RebalanceType,
     errors::StewardError,
-    maybe_transition_and_emit,
-    utils::{deserialize_stake_pool, get_stake_pool_address, get_validator_stake_info_at_index},
-    Config, Staker, StewardStateAccount,
+    events::{DecreaseComponents, RebalanceEvent, RebalanceTypeTag},
+    maybe_transition,
+    utils::{
+        deserialize_stake_pool, get_stake_pool_address, get_validator_stake_info_at_index,
+        state_checks,
+    },
+    Config, StewardStateAccount, StewardStateEnum,
 };
 
 #[derive(Accounts)]
@@ -51,13 +57,6 @@ pub struct Rebalance<'info> {
     #[account(address = get_stake_pool_address(&config)?)]
     pub stake_pool: AccountInfo<'info>,
 
-    #[account(
-        mut,
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
-
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(
         seeds = [
@@ -114,8 +113,7 @@ pub struct Rebalance<'info> {
     )]
     pub transient_stake_account: AccountInfo<'info>,
 
-    /// CHECK: passing through, checks are done by spl-stake-pool
-    #[account(owner = vote::program::ID)]
+    /// CHECK: We check the owning program in the handler
     pub vote_account: AccountInfo<'info>,
 
     /// CHECK: passing through, checks are done by spl-stake-pool
@@ -140,61 +138,78 @@ pub struct Rebalance<'info> {
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = stake::program::ID)]
     pub stake_program: AccountInfo<'info>,
-
-    #[account(mut)]
-    pub signer: Signer<'info>,
 }
 
 pub fn handler(ctx: Context<Rebalance>, validator_list_index: usize) -> Result<()> {
-    let config = ctx.accounts.config.load()?;
-    let mut state_account = ctx.accounts.state_account.load_mut()?;
     let validator_history = ctx.accounts.validator_history.load()?;
     let validator_list = &ctx.accounts.validator_list;
     let clock = Clock::get()?;
     let epoch_schedule = EpochSchedule::get()?;
+    let config = ctx.accounts.config.load()?;
 
-    let validator_stake_info =
-        get_validator_stake_info_at_index(validator_list, validator_list_index)?;
-    require!(
-        validator_stake_info.vote_account_address == validator_history.vote_account,
-        StewardError::ValidatorNotInList
-    );
-    let transient_seed = u64::from(validator_stake_info.transient_seed_suffix);
+    let rebalance_type: RebalanceType;
+    let transient_seed: u64;
 
-    if config.is_paused() {
-        return Err(StewardError::StateMachinePaused.into());
-    }
+    {
+        let mut state_account = ctx.accounts.state_account.load_mut()?;
 
-    let minimum_delegation = minimum_delegation(get_minimum_delegation()?);
-    let stake_rent = Rent::get()?.minimum_balance(StakeStateV2::size_of());
-
-    let result = {
-        let validator_list_data = &mut ctx.accounts.validator_list.try_borrow_mut_data()?;
-        let (_, validator_list) = ValidatorListHeader::deserialize_vec(validator_list_data)?;
-
-        let stake_pool_lamports_with_fixed_cost =
-            deserialize_stake_pool(&ctx.accounts.stake_pool)?.total_lamports;
-        let reserve_lamports_with_rent = ctx.accounts.reserve_stake.lamports();
-
-        state_account.state.rebalance(
-            clock.epoch,
-            validator_list_index,
-            &validator_list,
-            stake_pool_lamports_with_fixed_cost,
-            reserve_lamports_with_rent,
-            minimum_delegation,
-            stake_rent,
-            &config.parameters,
-        )?
-    };
+        state_checks(
+            &clock,
+            &config,
+            &state_account,
+            &ctx.accounts.validator_list,
+            Some(StewardStateEnum::Rebalance),
+        )?;
+
+        let validator_stake_info =
+            get_validator_stake_info_at_index(validator_list, validator_list_index)?;
+        require!(
+            validator_stake_info.vote_account_address == validator_history.vote_account,
+            StewardError::ValidatorNotInList
+        );
+
+        if ctx.accounts.vote_account.owner != &vote::program::ID
+            && !state_account
+                .state
+                .validators_to_remove
+                .get(validator_list_index)?
+        {
+            return Err(StewardError::ValidatorNeedsToBeMarkedForRemoval.into());
+        }
 
-    match result {
+        transient_seed = u64::from(validator_stake_info.transient_seed_suffix);
+
+        let minimum_delegation = minimum_delegation(get_minimum_delegation()?);
+        let stake_rent = Rent::get()?.minimum_balance(StakeStateV2::size_of());
+
+        rebalance_type = {
+            let validator_list_data = &mut ctx.accounts.validator_list.try_borrow_mut_data()?;
+            let (_, validator_list) = ValidatorListHeader::deserialize_vec(validator_list_data)?;
+
+            let stake_pool_lamports_with_fixed_cost =
+                deserialize_stake_pool(&ctx.accounts.stake_pool)?.total_lamports;
+            let reserve_lamports_with_rent = ctx.accounts.reserve_stake.lamports();
+
+            state_account.state.rebalance(
+                clock.epoch,
+                validator_list_index,
+                &validator_list,
+                stake_pool_lamports_with_fixed_cost,
+                reserve_lamports_with_rent,
+                minimum_delegation,
+                stake_rent,
+                &config.parameters,
+            )?
+        };
+    }
+
+    match rebalance_type.clone() {
         RebalanceType::Decrease(decrease_components) => {
             invoke_signed(
                 &spl_stake_pool::instruction::decrease_validator_stake_with_reserve(
                     &ctx.accounts.stake_pool_program.key(),
                     &ctx.accounts.stake_pool.key(),
-                    &ctx.accounts.staker.key(),
+                    &ctx.accounts.state_account.key(),
                     &ctx.accounts.withdraw_authority.key(),
                     &ctx.accounts.validator_list.key(),
                     &ctx.accounts.reserve_stake.key(),
@@ -205,7 +220,7 @@ pub fn handler(ctx: Context<Rebalance>, validator_list_index: usize) -> Result<(
                 ),
                 &[
                     ctx.accounts.stake_pool.to_account_info(),
-                    ctx.accounts.staker.to_account_info(),
+                    ctx.accounts.state_account.to_account_info(),
                     ctx.accounts.withdraw_authority.to_owned(),
                     ctx.accounts.validator_list.to_account_info(),
                     ctx.accounts.reserve_stake.to_account_info(),
@@ -218,9 +233,9 @@ pub fn handler(ctx: Context<Rebalance>, validator_list_index: usize) -> Result<(
                     ctx.accounts.stake_program.to_account_info(),
                 ],
                 &[&[
-                    Staker::SEED,
+                    StewardStateAccount::SEED,
                     &ctx.accounts.config.key().to_bytes(),
-                    &[ctx.accounts.staker.bump],
+                    &[ctx.bumps.state_account],
                 ]],
             )?;
         }
@@ -229,7 +244,7 @@ pub fn handler(ctx: Context<Rebalance>, validator_list_index: usize) -> Result<(
                 &spl_stake_pool::instruction::increase_validator_stake(
                     &ctx.accounts.stake_pool_program.key(),
                     &ctx.accounts.stake_pool.key(),
-                    &ctx.accounts.staker.key(),
+                    &ctx.accounts.state_account.key(),
                     &ctx.accounts.withdraw_authority.key(),
                     &ctx.accounts.validator_list.key(),
                     &ctx.accounts.reserve_stake.key(),
@@ -241,7 +256,7 @@ pub fn handler(ctx: Context<Rebalance>, validator_list_index: usize) -> Result<(
                 ),
                 &[
                     ctx.accounts.stake_pool.to_account_info(),
-                    ctx.accounts.staker.to_account_info(),
+                    ctx.accounts.state_account.to_account_info(),
                     ctx.accounts.withdraw_authority.to_owned(),
                     ctx.accounts.validator_list.to_account_info(),
                     ctx.accounts.reserve_stake.to_account_info(),
@@ -256,21 +271,63 @@ pub fn handler(ctx: Context<Rebalance>, validator_list_index: usize) -> Result<(
                     ctx.accounts.stake_program.to_account_info(),
                 ],
                 &[&[
-                    Staker::SEED,
+                    StewardStateAccount::SEED,
                     &ctx.accounts.config.key().to_bytes(),
-                    &[ctx.accounts.staker.bump],
+                    &[ctx.bumps.state_account],
                 ]],
             )?;
         }
         RebalanceType::None => {}
     }
 
-    maybe_transition_and_emit(
-        &mut state_account.state,
-        &clock,
-        &config.parameters,
-        &epoch_schedule,
-    )?;
+    {
+        let mut state_account = ctx.accounts.state_account.load_mut()?;
+
+        emit!(rebalance_to_event(
+            ctx.accounts.vote_account.key(),
+            clock.epoch as u16,
+            rebalance_type
+        ));
+
+        if let Some(event) = maybe_transition(
+            &mut state_account.state,
+            &clock,
+            &config.parameters,
+            &epoch_schedule,
+        )? {
+            emit!(event);
+        }
+    }
 
     Ok(())
 }
+
+fn rebalance_to_event(
+    vote_account: Pubkey,
+    epoch: u16,
+    rebalance_type: RebalanceType,
+) -> RebalanceEvent {
+    match rebalance_type {
+        RebalanceType::None => RebalanceEvent {
+            vote_account,
+            epoch,
+            rebalance_type_tag: RebalanceTypeTag::None,
+            increase_lamports: 0,
+            decrease_components: DecreaseComponents::default(),
+        },
+        RebalanceType::Increase(lamports) => RebalanceEvent {
+            vote_account,
+            epoch,
+            rebalance_type_tag: RebalanceTypeTag::Increase,
+            increase_lamports: lamports,
+            decrease_components: DecreaseComponents::default(),
+        },
+        RebalanceType::Decrease(decrease_components) => RebalanceEvent {
+            vote_account,
+            epoch,
+            rebalance_type_tag: RebalanceTypeTag::Decrease,
+            increase_lamports: 0,
+            decrease_components,
+        },
+    }
+}
diff --git a/programs/steward/src/instructions/remove_validator_from_blacklist.rs b/programs/steward/src/instructions/remove_validator_from_blacklist.rs
index 1c9c267e..c8e4c57e 100644
--- a/programs/steward/src/instructions/remove_validator_from_blacklist.rs
+++ b/programs/steward/src/instructions/remove_validator_from_blacklist.rs
@@ -1,4 +1,4 @@
-use crate::{utils::get_config_authority, Config};
+use crate::{utils::get_config_blacklist_authority, Config};
 use anchor_lang::prelude::*;
 
 #[derive(Accounts)]
@@ -6,14 +6,19 @@ pub struct RemoveValidatorFromBlacklist<'info> {
     #[account(mut)]
     pub config: AccountLoader<'info, Config>,
 
-    #[account(mut, address = get_config_authority(&config)?)]
+    #[account(mut, address = get_config_blacklist_authority(&config)?)]
     pub authority: Signer<'info>,
 }
 
 // Removes validator from blacklist. Validator will be eligible to receive delegation again when scores are recomputed.
 // Index is the index of the validator from ValidatorHistory.
-pub fn handler(ctx: Context<RemoveValidatorFromBlacklist>, index: u32) -> Result<()> {
+pub fn handler(
+    ctx: Context<RemoveValidatorFromBlacklist>,
+    validator_history_index: u32,
+) -> Result<()> {
     let mut config = ctx.accounts.config.load_mut()?;
-    config.blacklist.set(index as usize, false)?;
+    config
+        .validator_history_blacklist
+        .set(validator_history_index as usize, false)?;
     Ok(())
 }
diff --git a/programs/steward/src/instructions/reset_steward_state.rs b/programs/steward/src/instructions/reset_steward_state.rs
new file mode 100644
index 00000000..0588b014
--- /dev/null
+++ b/programs/steward/src/instructions/reset_steward_state.rs
@@ -0,0 +1,69 @@
+use crate::{
+    constants::{MAX_VALIDATORS, SORTED_INDEX_DEFAULT},
+    errors::StewardError,
+    state::{Config, StewardStateAccount},
+    utils::{deserialize_stake_pool, get_config_admin, get_stake_pool_address},
+    BitMask, Delegation, StewardStateEnum, STATE_PADDING_0_SIZE,
+};
+use anchor_lang::prelude::*;
+use spl_stake_pool::state::ValidatorListHeader;
+
+#[derive(Accounts)]
+pub struct ResetStewardState<'info> {
+    #[account(
+        mut,
+        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
+        bump
+    )]
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
+
+    pub config: AccountLoader<'info, Config>,
+
+    /// CHECK: Correct account guaranteed if address is correct
+    #[account(address = get_stake_pool_address(&config)?)]
+    pub stake_pool: AccountInfo<'info>,
+
+    /// CHECK: Correct account guaranteed if address is correct
+    #[account(address = deserialize_stake_pool(&stake_pool)?.validator_list)]
+    pub validator_list: AccountInfo<'info>,
+
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub authority: Signer<'info>,
+}
+
+/*
+    Resets steward state account to its initial state.
+*/
+pub fn handler(ctx: Context<ResetStewardState>) -> Result<()> {
+    let mut state_account = ctx.accounts.state_account.load_mut()?;
+
+    let clock = Clock::get()?;
+    state_account.is_initialized = true.into();
+    state_account.bump = ctx.bumps.state_account;
+
+    let config = ctx.accounts.config.load()?;
+    let validator_list_data = &mut ctx.accounts.validator_list.try_borrow_mut_data()?;
+    let (_, validator_list) = ValidatorListHeader::deserialize_vec(validator_list_data)?;
+
+    state_account.state.state_tag = StewardStateEnum::ComputeScores;
+    state_account.state.num_pool_validators = validator_list.len() as u64;
+    state_account.state.scores = [0; MAX_VALIDATORS];
+    state_account.state.sorted_score_indices = [SORTED_INDEX_DEFAULT; MAX_VALIDATORS];
+    state_account.state.yield_scores = [0; MAX_VALIDATORS];
+    state_account.state.sorted_yield_score_indices = [SORTED_INDEX_DEFAULT; MAX_VALIDATORS];
+    state_account.state.progress = BitMask::default();
+    state_account.state.current_epoch = clock.epoch;
+    state_account.state.next_cycle_epoch = clock
+        .epoch
+        .checked_add(config.parameters.num_epochs_between_scoring)
+        .ok_or(StewardError::ArithmeticError)?;
+    state_account.state.delegations = [Delegation::default(); MAX_VALIDATORS];
+    state_account.state.instant_unstake = BitMask::default();
+    state_account.state.start_computing_scores_slot = clock.slot;
+    state_account.state.validators_to_remove = BitMask::default();
+    state_account.state.validators_for_immediate_removal = BitMask::default();
+    state_account.state.validators_added = 0;
+    state_account.state.clear_flags();
+    state_account.state._padding0 = [0; STATE_PADDING_0_SIZE];
+    Ok(())
+}
diff --git a/programs/steward/src/instructions/resume_steward.rs b/programs/steward/src/instructions/resume_steward.rs
index 0b4a6a23..d1b392ce 100644
--- a/programs/steward/src/instructions/resume_steward.rs
+++ b/programs/steward/src/instructions/resume_steward.rs
@@ -1,13 +1,13 @@
 use anchor_lang::prelude::*;
 
-use crate::{utils::get_config_authority, Config};
+use crate::{utils::get_config_admin, Config};
 
 #[derive(Accounts)]
 pub struct ResumeSteward<'info> {
     #[account(mut)]
     pub config: AccountLoader<'info, Config>,
 
-    #[account(mut, address = get_config_authority(&config)?)]
+    #[account(mut, address = get_config_admin(&config)?)]
     pub authority: Signer<'info>,
 }
 
diff --git a/programs/steward/src/instructions/set_new_authority.rs b/programs/steward/src/instructions/set_new_authority.rs
index cc268ca2..0e82fbbe 100644
--- a/programs/steward/src/instructions/set_new_authority.rs
+++ b/programs/steward/src/instructions/set_new_authority.rs
@@ -1,7 +1,65 @@
+use anchor_lang::idl::types::*;
 use anchor_lang::prelude::*;
+use anchor_lang::IdlBuild;
+use borsh::{BorshDeserialize, BorshSerialize};
 
 use crate::{errors::StewardError, state::Config};
 
+#[repr(u8)]
+#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq)]
+pub enum AuthorityType {
+    SetAdmin = 0,
+    SetBlacklistAuthority = 1,
+    SetParametersAuthority = 2,
+}
+
+impl AuthorityType {
+    pub fn to_u8(self) -> u8 {
+        self as u8
+    }
+}
+
+// Implement IdlBuild for AuthorityType
+impl IdlBuild for AuthorityType {
+    fn create_type() -> Option<IdlTypeDef> {
+        Some(IdlTypeDef {
+            name: "AuthorityType".to_string(),
+            ty: IdlTypeDefTy::Enum {
+                variants: vec![
+                    IdlEnumVariant {
+                        name: "SetAdmin".to_string(),
+                        fields: Some(IdlDefinedFields::Named(vec![IdlField {
+                            name: "SetAdmin".to_string(),
+                            docs: Default::default(),
+                            ty: IdlType::Option(Box::new(IdlType::U8)),
+                        }])),
+                    },
+                    IdlEnumVariant {
+                        name: "SetBlacklistAuthority".to_string(),
+                        fields: Some(IdlDefinedFields::Named(vec![IdlField {
+                            name: "SetBlacklistAuthority".to_string(),
+                            docs: Default::default(),
+                            ty: IdlType::Option(Box::new(IdlType::U8)),
+                        }])),
+                    },
+                    IdlEnumVariant {
+                        name: "SetParameterAuthority".to_string(),
+                        fields: Some(IdlDefinedFields::Named(vec![IdlField {
+                            name: "SetParameterAuthority".to_string(),
+                            docs: Default::default(),
+                            ty: IdlType::Option(Box::new(IdlType::U8)),
+                        }])),
+                    },
+                ],
+            },
+            docs: Default::default(),
+            generics: Default::default(),
+            serialization: Default::default(),
+            repr: Default::default(),
+        })
+    }
+}
+
 #[derive(Accounts)]
 pub struct SetNewAuthority<'info> {
     #[account(mut)]
@@ -11,15 +69,26 @@ pub struct SetNewAuthority<'info> {
     pub new_authority: AccountInfo<'info>,
 
     #[account(mut)]
-    pub authority: Signer<'info>,
+    pub admin: Signer<'info>,
 }
 
-pub fn handler(ctx: Context<SetNewAuthority>) -> Result<()> {
+pub fn handler(ctx: Context<SetNewAuthority>, authority_type: AuthorityType) -> Result<()> {
     let mut config = ctx.accounts.config.load_mut()?;
-    if config.authority != *ctx.accounts.authority.key {
+    if config.admin != *ctx.accounts.admin.key {
         return Err(StewardError::Unauthorized.into());
     }
 
-    config.authority = ctx.accounts.new_authority.key();
+    match authority_type {
+        AuthorityType::SetAdmin => {
+            config.admin = ctx.accounts.new_authority.key();
+        }
+        AuthorityType::SetBlacklistAuthority => {
+            config.blacklist_authority = ctx.accounts.new_authority.key();
+        }
+        AuthorityType::SetParametersAuthority => {
+            config.parameters_authority = ctx.accounts.new_authority.key();
+        }
+    }
+
     Ok(())
 }
diff --git a/programs/steward/src/instructions/spl_passthrough.rs b/programs/steward/src/instructions/spl_passthrough.rs
index ed4224a8..b9b2bfaa 100644
--- a/programs/steward/src/instructions/spl_passthrough.rs
+++ b/programs/steward/src/instructions/spl_passthrough.rs
@@ -6,9 +6,9 @@
 
 use crate::constants::MAX_VALIDATORS;
 use crate::errors::StewardError;
-use crate::state::{Config, Staker};
+use crate::state::Config;
 use crate::utils::{
-    deserialize_stake_pool, get_config_authority, get_stake_pool_address,
+    deserialize_stake_pool, get_config_admin, get_stake_pool_address,
     get_validator_stake_info_at_index,
 };
 use crate::StewardStateAccount;
@@ -16,13 +16,20 @@ use anchor_lang::prelude::*;
 use anchor_lang::solana_program::{program::invoke_signed, stake, sysvar, vote};
 use spl_stake_pool::find_stake_program_address;
 use spl_stake_pool::instruction::PreferredValidatorType;
-use spl_stake_pool::state::ValidatorListHeader;
+use spl_stake_pool::state::{StakeStatus, ValidatorListHeader};
 use std::num::NonZeroU32;
 use validator_history::ValidatorHistory;
 
 #[derive(Accounts)]
 pub struct AddValidatorToPool<'info> {
     pub config: AccountLoader<'info, Config>,
+
+    #[account(
+        mut,
+        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
+        bump
+    )]
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
     /// CHECK: CPI program
     #[account(
         address = spl_stake_pool::ID
@@ -34,11 +41,7 @@ pub struct AddValidatorToPool<'info> {
     )]
     /// CHECK: passing through, checks are done by spl-stake-pool
     pub stake_pool: AccountInfo<'info>,
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
+
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(mut)]
     pub reserve_stake: AccountInfo<'info>,
@@ -65,8 +68,8 @@ pub struct AddValidatorToPool<'info> {
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = stake::program::ID)]
     pub stake_program: AccountInfo<'info>,
-    #[account(mut, address = get_config_authority(&config)?)]
-    pub signer: Signer<'info>,
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub admin: Signer<'info>,
 }
 
 pub fn add_validator_to_pool_handler(
@@ -74,18 +77,32 @@ pub fn add_validator_to_pool_handler(
     validator_seed: Option<u32>,
 ) -> Result<()> {
     {
-        let validator_list_data = &mut ctx.accounts.validator_list.try_borrow_mut_data()?;
-        let (_, validator_list) = ValidatorListHeader::deserialize_vec(validator_list_data)?;
+        let mut state_account = ctx.accounts.state_account.load_mut()?;
+        let epoch = Clock::get()?.epoch;
 
-        if validator_list.len().checked_add(1).unwrap() > MAX_VALIDATORS as u32 {
-            return Err(StewardError::MaxValidatorsReached.into());
+        // Should not be able to add a validator if update is not complete
+        require!(
+            epoch == state_account.state.current_epoch,
+            StewardError::EpochMaintenanceNotComplete
+        );
+
+        {
+            let validator_list_data = &mut ctx.accounts.validator_list.try_borrow_mut_data()?;
+            let (_, validator_list) = ValidatorListHeader::deserialize_vec(validator_list_data)?;
+
+            if validator_list.len().checked_add(1).unwrap() > MAX_VALIDATORS as u32 {
+                return Err(StewardError::MaxValidatorsReached.into());
+            }
         }
+
+        state_account.state.increment_validator_to_add()?;
     }
+
     invoke_signed(
         &spl_stake_pool::instruction::add_validator_to_pool(
             &ctx.accounts.stake_pool_program.key(),
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.state_account.key(),
             &ctx.accounts.reserve_stake.key(),
             &ctx.accounts.withdraw_authority.key(),
             &ctx.accounts.validator_list.key(),
@@ -95,7 +112,7 @@ pub fn add_validator_to_pool_handler(
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.state_account.to_account_info(),
             ctx.accounts.reserve_stake.to_owned(),
             ctx.accounts.withdraw_authority.to_owned(),
             ctx.accounts.validator_list.to_account_info(),
@@ -109,9 +126,9 @@ pub fn add_validator_to_pool_handler(
             ctx.accounts.stake_program.to_account_info(),
         ],
         &[&[
-            Staker::SEED,
+            StewardStateAccount::SEED,
             &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
+            &[ctx.bumps.state_account],
         ]],
     )?;
     Ok(())
@@ -125,7 +142,7 @@ pub struct RemoveValidatorFromPool<'info> {
         seeds = [StewardStateAccount::SEED, config.key().as_ref()],
         bump
     )]
-    pub steward_state: AccountLoader<'info, StewardStateAccount>,
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
 
     /// CHECK: CPI program
     #[account(
@@ -138,15 +155,11 @@ pub struct RemoveValidatorFromPool<'info> {
         address = get_stake_pool_address(&config)?
     )]
     pub stake_pool: AccountInfo<'info>,
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
+
     /// CHECK: passing through, checks are done by spl-stake-pool
     pub withdraw_authority: AccountInfo<'info>,
     /// CHECK: passing through, checks are done by spl-stake-pool
-    #[account(mut)]
+    #[account(mut, address = deserialize_stake_pool(&stake_pool)?.validator_list)]
     pub validator_list: AccountInfo<'info>,
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(mut)]
@@ -159,41 +172,48 @@ pub struct RemoveValidatorFromPool<'info> {
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = stake::program::ID)]
     pub stake_program: AccountInfo<'info>,
-    #[account(mut, address = get_config_authority(&config)?)]
-    pub signer: Signer<'info>,
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub admin: Signer<'info>,
 }
 
 pub fn remove_validator_from_pool_handler(
     ctx: Context<RemoveValidatorFromPool>,
     validator_list_index: usize,
 ) -> Result<()> {
-    let mut state_account = ctx.accounts.steward_state.load_mut()?;
-
-    if validator_list_index < state_account.state.num_pool_validators as usize {
-        let validator_list_stake_info = get_validator_stake_info_at_index(
-            &ctx.accounts.validator_list.to_account_info(),
-            validator_list_index,
-        )?;
+    {
+        let state_account = ctx.accounts.state_account.load_mut()?;
+        let epoch = Clock::get()?.epoch;
 
-        let (validator_list_stake_account, _) = find_stake_program_address(
-            &ctx.accounts.stake_pool_program.key(),
-            &validator_list_stake_info.vote_account_address,
-            &ctx.accounts.stake_pool.key(),
-            NonZeroU32::new(u32::from(validator_list_stake_info.validator_seed_suffix)),
+        // Should not be able to remove a validator if update is not complete
+        require!(
+            epoch == state_account.state.current_epoch,
+            StewardError::EpochMaintenanceNotComplete
         );
 
-        if validator_list_stake_account != ctx.accounts.stake_account.key() {
-            return Err(StewardError::ValidatorNotInList.into());
+        if validator_list_index < state_account.state.num_pool_validators as usize {
+            let validator_list_stake_info = get_validator_stake_info_at_index(
+                &ctx.accounts.validator_list.to_account_info(),
+                validator_list_index,
+            )?;
+
+            let (validator_list_stake_account, _) = find_stake_program_address(
+                &ctx.accounts.stake_pool_program.key(),
+                &validator_list_stake_info.vote_account_address,
+                &ctx.accounts.stake_pool.key(),
+                NonZeroU32::new(u32::from(validator_list_stake_info.validator_seed_suffix)),
+            );
+
+            if validator_list_stake_account != ctx.accounts.stake_account.key() {
+                return Err(StewardError::ValidatorNotInList.into());
+            }
         }
-
-        state_account.state.remove_validator(validator_list_index)?;
     }
 
     invoke_signed(
         &spl_stake_pool::instruction::remove_validator_from_pool(
             &ctx.accounts.stake_pool_program.key(),
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.state_account.key(),
             &ctx.accounts.withdraw_authority.key(),
             &ctx.accounts.validator_list.key(),
             &ctx.accounts.stake_account.key(),
@@ -201,7 +221,7 @@ pub fn remove_validator_from_pool_handler(
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.state_account.to_account_info(),
             ctx.accounts.withdraw_authority.to_owned(),
             ctx.accounts.validator_list.to_account_info(),
             ctx.accounts.stake_account.to_account_info(),
@@ -210,17 +230,50 @@ pub fn remove_validator_from_pool_handler(
             ctx.accounts.stake_program.to_account_info(),
         ],
         &[&[
-            Staker::SEED,
+            StewardStateAccount::SEED,
             &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
+            &[ctx.bumps.state_account],
         ]],
     )?;
+
+    {
+        // Read the state account again
+        let mut state_account = ctx.accounts.state_account.load_mut()?;
+        let validator_list = &ctx.accounts.validator_list;
+        let validator_stake_info =
+            get_validator_stake_info_at_index(validator_list, validator_list_index)?;
+
+        let stake_status = StakeStatus::try_from(validator_stake_info.status)?;
+
+        match stake_status {
+            StakeStatus::Active => {
+                // Should never happen
+                return Err(StewardError::ValidatorMarkedActive.into());
+            }
+            StakeStatus::DeactivatingValidator | StakeStatus::ReadyForRemoval => {
+                state_account
+                    .state
+                    .mark_validator_for_immediate_removal(validator_list_index)?;
+            }
+            StakeStatus::DeactivatingAll | StakeStatus::DeactivatingTransient => {
+                state_account
+                    .state
+                    .mark_validator_for_removal(validator_list_index)?;
+            }
+        }
+    }
     Ok(())
 }
 
 #[derive(Accounts)]
 pub struct SetPreferredValidator<'info> {
     pub config: AccountLoader<'info, Config>,
+    #[account(
+        mut,
+        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
+        bump
+    )]
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
     /// CHECK: CPI program
     #[account(
         address = spl_stake_pool::ID
@@ -232,16 +285,12 @@ pub struct SetPreferredValidator<'info> {
         address = get_stake_pool_address(&config)?
     )]
     pub stake_pool: AccountInfo<'info>,
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
+
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = deserialize_stake_pool(&stake_pool)?.validator_list)]
     pub validator_list: AccountInfo<'info>,
-    #[account(mut, address = get_config_authority(&config)?)]
-    pub signer: Signer<'info>,
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub admin: Signer<'info>,
 }
 
 pub fn set_preferred_validator_handler(
@@ -253,20 +302,20 @@ pub fn set_preferred_validator_handler(
         &spl_stake_pool::instruction::set_preferred_validator(
             ctx.accounts.stake_pool_program.key,
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.state_account.key(),
             &ctx.accounts.validator_list.key(),
             validator_type.clone(),
             validator,
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.state_account.to_account_info(),
             ctx.accounts.validator_list.to_account_info(),
         ],
         &[&[
-            Staker::SEED,
+            StewardStateAccount::SEED,
             &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
+            &[ctx.bumps.state_account],
         ]],
     )?;
     Ok(())
@@ -280,7 +329,7 @@ pub struct IncreaseValidatorStake<'info> {
         seeds = [StewardStateAccount::SEED, config.key().as_ref()],
         bump
     )]
-    pub steward_state: AccountLoader<'info, StewardStateAccount>,
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
     #[account(
         mut,
         seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()],
@@ -299,15 +348,11 @@ pub struct IncreaseValidatorStake<'info> {
         address = get_stake_pool_address(&config)?
     )]
     pub stake_pool: AccountInfo<'info>,
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
+
     /// CHECK: passing through, checks are done by spl-stake-pool
     pub withdraw_authority: AccountInfo<'info>,
     /// CHECK: passing through, checks are done by spl-stake-pool
-    #[account(mut)]
+    #[account(mut, address = deserialize_stake_pool(&stake_pool)?.validator_list)]
     pub validator_list: AccountInfo<'info>,
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(
@@ -336,19 +381,17 @@ pub struct IncreaseValidatorStake<'info> {
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = stake::program::ID)]
     pub stake_program: AccountInfo<'info>,
-    #[account(mut, address = get_config_authority(&config)?)]
-    pub signer: Signer<'info>,
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub admin: Signer<'info>,
 }
-
 pub fn increase_validator_stake_handler(
     ctx: Context<IncreaseValidatorStake>,
     lamports: u64,
     transient_seed: u64,
 ) -> Result<()> {
-    let validator_history = ctx.accounts.validator_history.load()?;
-
     {
-        let mut state_account = ctx.accounts.steward_state.load_mut()?;
+        let validator_history = ctx.accounts.validator_history.load()?;
+        let mut state_account = ctx.accounts.state_account.load_mut()?;
         // Get the balance
         let balance = state_account
             .state
@@ -366,7 +409,7 @@ pub fn increase_validator_stake_handler(
         &spl_stake_pool::instruction::increase_validator_stake(
             &ctx.accounts.stake_pool_program.key(),
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.state_account.key(),
             &ctx.accounts.withdraw_authority.key(),
             &ctx.accounts.validator_list.key(),
             &ctx.accounts.reserve_stake.key(),
@@ -378,7 +421,7 @@ pub fn increase_validator_stake_handler(
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.state_account.to_account_info(),
             ctx.accounts.withdraw_authority.to_owned(),
             ctx.accounts.validator_list.to_account_info(),
             ctx.accounts.reserve_stake.to_account_info(),
@@ -393,9 +436,9 @@ pub fn increase_validator_stake_handler(
             ctx.accounts.stake_program.to_account_info(),
         ],
         &[&[
-            Staker::SEED,
+            StewardStateAccount::SEED,
             &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
+            &[ctx.bumps.state_account],
         ]],
     )?;
     Ok(())
@@ -409,7 +452,7 @@ pub struct DecreaseValidatorStake<'info> {
         seeds = [StewardStateAccount::SEED, config.key().as_ref()],
         bump
     )]
-    pub steward_state: AccountLoader<'info, StewardStateAccount>,
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
     #[account(
         mut,
         seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()],
@@ -428,15 +471,11 @@ pub struct DecreaseValidatorStake<'info> {
         address = get_stake_pool_address(&config)?
     )]
     pub stake_pool: AccountInfo<'info>,
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
+
     /// CHECK: passing through, checks are done by spl-stake-pool
     pub withdraw_authority: AccountInfo<'info>,
     /// CHECK: passing through, checks are done by spl-stake-pool
-    #[account(mut)]
+    #[account(mut, address = deserialize_stake_pool(&stake_pool)?.validator_list)]
     pub validator_list: AccountInfo<'info>,
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(
@@ -462,8 +501,8 @@ pub struct DecreaseValidatorStake<'info> {
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = stake::program::ID)]
     pub stake_program: AccountInfo<'info>,
-    #[account(mut, address = get_config_authority(&config)?)]
-    pub signer: Signer<'info>,
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub admin: Signer<'info>,
 }
 
 pub fn decrease_validator_stake_handler(
@@ -471,10 +510,9 @@ pub fn decrease_validator_stake_handler(
     lamports: u64,
     transient_seed: u64,
 ) -> Result<()> {
-    let validator_history = ctx.accounts.validator_history.load()?;
-
     {
-        let mut state_account = ctx.accounts.steward_state.load_mut()?;
+        let validator_history = ctx.accounts.validator_history.load()?;
+        let mut state_account = ctx.accounts.state_account.load_mut()?;
         // Get the balance
         let balance = state_account
             .state
@@ -492,7 +530,7 @@ pub fn decrease_validator_stake_handler(
         &spl_stake_pool::instruction::decrease_validator_stake_with_reserve(
             &ctx.accounts.stake_pool_program.key(),
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.state_account.key(),
             &ctx.accounts.withdraw_authority.key(),
             &ctx.accounts.validator_list.key(),
             &ctx.accounts.reserve_stake.key(),
@@ -503,7 +541,7 @@ pub fn decrease_validator_stake_handler(
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.state_account.to_account_info(),
             ctx.accounts.withdraw_authority.to_owned(),
             ctx.accounts.validator_list.to_account_info(),
             ctx.accounts.reserve_stake.to_account_info(),
@@ -516,9 +554,9 @@ pub fn decrease_validator_stake_handler(
             ctx.accounts.stake_program.to_account_info(),
         ],
         &[&[
-            Staker::SEED,
+            StewardStateAccount::SEED,
             &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
+            &[ctx.bumps.state_account],
         ]],
     )?;
     Ok(())
@@ -532,7 +570,7 @@ pub struct IncreaseAdditionalValidatorStake<'info> {
         seeds = [StewardStateAccount::SEED, config.key().as_ref()],
         bump
     )]
-    pub steward_state: AccountLoader<'info, StewardStateAccount>,
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
     #[account(
         mut,
         seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()],
@@ -550,11 +588,7 @@ pub struct IncreaseAdditionalValidatorStake<'info> {
         address = get_stake_pool_address(&config)?
     )]
     pub stake_pool: AccountInfo<'info>,
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
+
     /// CHECK: passing through, checks are done by spl-stake-pool
     pub withdraw_authority: AccountInfo<'info>,
     /// CHECK: passing through, checks are done by spl-stake-pool
@@ -586,8 +620,8 @@ pub struct IncreaseAdditionalValidatorStake<'info> {
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = stake::program::ID)]
     pub stake_program: AccountInfo<'info>,
-    #[account(mut, address = get_config_authority(&config)?)]
-    pub signer: Signer<'info>,
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub admin: Signer<'info>,
 }
 
 pub fn increase_additional_validator_stake_handler(
@@ -596,10 +630,9 @@ pub fn increase_additional_validator_stake_handler(
     transient_seed: u64,
     ephemeral_seed: u64,
 ) -> Result<()> {
-    let validator_history = ctx.accounts.validator_history.load()?;
-
     {
-        let mut state_account = ctx.accounts.steward_state.load_mut()?;
+        let validator_history = ctx.accounts.validator_history.load()?;
+        let mut state_account = ctx.accounts.state_account.load_mut()?;
         // Get the balance
         let balance = state_account
             .state
@@ -617,7 +650,7 @@ pub fn increase_additional_validator_stake_handler(
         &spl_stake_pool::instruction::increase_additional_validator_stake(
             &ctx.accounts.stake_pool_program.key(),
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.state_account.key(),
             &ctx.accounts.withdraw_authority.key(),
             &ctx.accounts.validator_list.key(),
             &ctx.accounts.reserve_stake.key(),
@@ -631,7 +664,7 @@ pub fn increase_additional_validator_stake_handler(
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.state_account.to_account_info(),
             ctx.accounts.withdraw_authority.to_owned(),
             ctx.accounts.validator_list.to_account_info(),
             ctx.accounts.reserve_stake.to_account_info(),
@@ -646,9 +679,9 @@ pub fn increase_additional_validator_stake_handler(
             ctx.accounts.stake_program.to_account_info(),
         ],
         &[&[
-            Staker::SEED,
+            StewardStateAccount::SEED,
             &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
+            &[ctx.bumps.state_account],
         ]],
     )?;
     Ok(())
@@ -662,7 +695,7 @@ pub struct DecreaseAdditionalValidatorStake<'info> {
         seeds = [StewardStateAccount::SEED, config.key().as_ref()],
         bump
     )]
-    pub steward_state: AccountLoader<'info, StewardStateAccount>,
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
     #[account(
         mut,
         seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()],
@@ -685,11 +718,7 @@ pub struct DecreaseAdditionalValidatorStake<'info> {
         address = get_stake_pool_address(&config)?
     )]
     pub stake_pool: AccountInfo<'info>,
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
+
     /// CHECK: passing through, checks are done by spl-stake-pool
     pub withdraw_authority: AccountInfo<'info>,
     /// CHECK: passing through, checks are done by spl-stake-pool
@@ -715,8 +744,8 @@ pub struct DecreaseAdditionalValidatorStake<'info> {
     /// CHECK: passing through, checks are done by spl-stake-pool
     #[account(address = stake::program::ID)]
     pub stake_program: AccountInfo<'info>,
-    #[account(mut, address = get_config_authority(&config)?)]
-    pub signer: Signer<'info>,
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub admin: Signer<'info>,
 }
 
 pub fn decrease_additional_validator_stake_handler(
@@ -725,10 +754,9 @@ pub fn decrease_additional_validator_stake_handler(
     transient_seed: u64,
     ephemeral_seed: u64,
 ) -> Result<()> {
-    let validator_history = ctx.accounts.validator_history.load()?;
-
     {
-        let mut state_account = ctx.accounts.steward_state.load_mut()?;
+        let validator_history = ctx.accounts.validator_history.load()?;
+        let mut state_account = ctx.accounts.state_account.load_mut()?;
         // Get the balance
         let balance = state_account
             .state
@@ -746,7 +774,7 @@ pub fn decrease_additional_validator_stake_handler(
         &spl_stake_pool::instruction::decrease_additional_validator_stake(
             &ctx.accounts.stake_pool_program.key(),
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.state_account.key(),
             &ctx.accounts.withdraw_authority.key(),
             &ctx.accounts.validator_list.key(),
             &ctx.accounts.reserve_stake.key(),
@@ -759,7 +787,7 @@ pub fn decrease_additional_validator_stake_handler(
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.state_account.to_account_info(),
             ctx.accounts.withdraw_authority.to_owned(),
             ctx.accounts.validator_list.to_account_info(),
             ctx.accounts.reserve_stake.to_account_info(),
@@ -772,9 +800,9 @@ pub fn decrease_additional_validator_stake_handler(
             ctx.accounts.stake_program.to_account_info(),
         ],
         &[&[
-            Staker::SEED,
+            StewardStateAccount::SEED,
             &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
+            &[ctx.bumps.state_account],
         ]],
     )?;
     Ok(())
@@ -783,6 +811,12 @@ pub fn decrease_additional_validator_stake_handler(
 #[derive(Accounts)]
 pub struct SetStaker<'info> {
     pub config: AccountLoader<'info, Config>,
+    #[account(
+        mut,
+        seeds = [StewardStateAccount::SEED, config.key().as_ref()],
+        bump
+    )]
+    pub state_account: AccountLoader<'info, StewardStateAccount>,
     /// CHECK: CPI program
     #[account(
         address = spl_stake_pool::ID
@@ -793,15 +827,11 @@ pub struct SetStaker<'info> {
         mut, address = get_stake_pool_address(&config)?
     )]
     pub stake_pool: AccountInfo<'info>,
-    #[account(
-        seeds = [Staker::SEED, config.key().as_ref()],
-        bump = staker.bump
-    )]
-    pub staker: Account<'info, Staker>,
+
     /// CHECK: passing through, checks are done by spl-stake-pool
     pub new_staker: AccountInfo<'info>,
-    #[account(mut, address = get_config_authority(&config)?)]
-    pub signer: Signer<'info>,
+    #[account(mut, address = get_config_admin(&config)?)]
+    pub admin: Signer<'info>,
 }
 
 /// Note this function can only be called once by the Steward, as it will lose it's authority
@@ -812,18 +842,18 @@ pub fn set_staker_handler(ctx: Context<SetStaker>) -> Result<()> {
         &spl_stake_pool::instruction::set_staker(
             &ctx.accounts.stake_pool_program.key(),
             &ctx.accounts.stake_pool.key(),
-            &ctx.accounts.staker.key(),
+            &ctx.accounts.state_account.key(),
             &ctx.accounts.new_staker.key(),
         ),
         &[
             ctx.accounts.stake_pool.to_account_info(),
-            ctx.accounts.staker.to_account_info(),
+            ctx.accounts.state_account.to_account_info(),
             ctx.accounts.new_staker.to_account_info(),
         ],
         &[&[
-            Staker::SEED,
+            StewardStateAccount::SEED,
             &ctx.accounts.config.key().to_bytes(),
-            &[ctx.accounts.staker.bump],
+            &[ctx.bumps.state_account],
         ]],
     )?;
     Ok(())
diff --git a/programs/steward/src/instructions/update_parameters.rs b/programs/steward/src/instructions/update_parameters.rs
index 97032210..02e7f5d9 100644
--- a/programs/steward/src/instructions/update_parameters.rs
+++ b/programs/steward/src/instructions/update_parameters.rs
@@ -1,4 +1,4 @@
-use crate::{utils::get_config_authority, Config, UpdateParametersArgs};
+use crate::{utils::get_config_parameter_authority, Config, UpdateParametersArgs};
 use anchor_lang::prelude::*;
 
 #[derive(Accounts)]
@@ -6,7 +6,7 @@ pub struct UpdateParameters<'info> {
     #[account(mut)]
     pub config: AccountLoader<'info, Config>,
 
-    #[account(mut, address = get_config_authority(&config)?)]
+    #[account(mut, address = get_config_parameter_authority(&config)?)]
     pub authority: Signer<'info>,
 }
 
diff --git a/programs/steward/src/lib.rs b/programs/steward/src/lib.rs
index 493e622e..67b9ae69 100644
--- a/programs/steward/src/lib.rs
+++ b/programs/steward/src/lib.rs
@@ -9,6 +9,7 @@ mod allocator;
 pub mod constants;
 pub mod delegation;
 pub mod errors;
+pub mod events;
 pub mod instructions;
 pub mod score;
 pub mod state;
@@ -29,15 +30,25 @@ To initialize a Steward-managed pool:
 3) `realloc_state` - increases the size of the State account to StewardStateAccount::SIZE, and initializes values once at that size
 
 Each cycle, the following steps are performed by a permissionless cranker:
-1) compute_score (once per validator)
+x) epoch_maintenance ( once per epoch )
+1) compute_score ( once per validator )
 2) compute_delegations
 3) idle
-4) compute_instant_unstake (once per validator)
-5) rebalance (once per validator)
+4) compute_instant_unstake ( once per validator )
+5) rebalance ( once per validator )
 
 For the remaining epochs in a cycle, the state will repeat idle->compute_instant_unstake->rebalance.
 After `num_epochs_between_scoring` epochs, the state can transition back to ComputeScores.
 
+To manage the validators in the pool, there are the following permissionless instructions:
+- `auto_add_validator_to_pool`
+- `auto_remove_validator_from_pool`
+- `instant_remove_validator` - called when a validator can be removed within the same epoch it was marked for removal
+
+There are three authorities within the program:
+- `admin` - can update authority, pause, resume, and reset state
+- `parameters_authority` - can update parameters
+- `blacklist_authority` - can add and remove validators from the blacklist
 
 If manual intervention is required, the following spl-stake-pool instructions are available, and can be executed by the config.authority:
 - `add_validator_to_pool`
@@ -47,7 +58,6 @@ If manual intervention is required, the following spl-stake-pool instructions ar
 - `decrease_validator_stake`
 - `increase_additional_validator_stake`
 - `decrease_additional_validator_stake`
-- `redelegate`
 - `set_staker`
 */
 #[program]
@@ -58,17 +68,11 @@ pub mod steward {
 
     // Initializes Config and Staker accounts. Must be called before any other instruction
     // Requires Pool to be initialized
-    pub fn initialize_config(
-        ctx: Context<InitializeConfig>,
-        authority: Pubkey,
+    pub fn initialize_steward(
+        ctx: Context<InitializeSteward>,
         update_parameters_args: UpdateParametersArgs,
     ) -> Result<()> {
-        instructions::initialize_config::handler(ctx, authority, &update_parameters_args)
-    }
-
-    /// Creates state account
-    pub const fn initialize_state(ctx: Context<InitializeState>) -> Result<()> {
-        instructions::initialize_state::handler(ctx)
+        instructions::initialize_steward::handler(ctx, &update_parameters_args)
     }
 
     /// Increases state account by 10KiB each ix until it reaches StewardStateAccount::SIZE
@@ -91,6 +95,22 @@ pub mod steward {
         instructions::auto_remove_validator_from_pool::handler(ctx, validator_list_index as usize)
     }
 
+    /// When a validator is marked for immediate removal, it needs to be removed before normal functions can continue
+    pub fn instant_remove_validator(
+        ctx: Context<InstantRemoveValidator>,
+        validator_index_to_remove: u64,
+    ) -> Result<()> {
+        instructions::instant_remove_validator::handler(ctx, validator_index_to_remove as usize)
+    }
+
+    /// Housekeeping, run at the start of any new epoch before any other instructions
+    pub fn epoch_maintenance(
+        ctx: Context<EpochMaintenance>,
+        validator_index_to_remove: Option<u64>,
+    ) -> Result<()> {
+        instructions::epoch_maintenance::handler(ctx, validator_index_to_remove.map(|x| x as usize))
+    }
+
     /// Computes score for a the validator at `validator_list_index` for the current cycle.
     pub fn compute_score(ctx: Context<ComputeScore>, validator_list_index: u64) -> Result<()> {
         instructions::compute_score::handler(ctx, validator_list_index as usize)
@@ -125,14 +145,19 @@ pub mod steward {
 
     // If `new_authority` is not a pubkey you own, you cannot regain the authority, but you can
     // use the stake pool manager to set a new staker
-    pub fn set_new_authority(ctx: Context<SetNewAuthority>) -> Result<()> {
-        instructions::set_new_authority::handler(ctx)
+    pub fn set_new_authority(
+        ctx: Context<SetNewAuthority>,
+        authority_type: AuthorityType,
+    ) -> Result<()> {
+        instructions::set_new_authority::handler(ctx, authority_type)
     }
 
+    /// Pauses the steward, preventing any further state transitions
     pub fn pause_steward(ctx: Context<PauseSteward>) -> Result<()> {
         instructions::pause_steward::handler(ctx)
     }
 
+    /// Resumes the steward, allowing state transitions to continue
     pub fn resume_steward(ctx: Context<ResumeSteward>) -> Result<()> {
         instructions::resume_steward::handler(ctx)
     }
@@ -140,17 +165,17 @@ pub mod steward {
     /// Adds the validator at `index` to the blacklist. It will be instant unstaked and never receive delegations
     pub fn add_validator_to_blacklist(
         ctx: Context<AddValidatorToBlacklist>,
-        index: u32,
+        validator_history_blacklist: u32,
     ) -> Result<()> {
-        instructions::add_validator_to_blacklist::handler(ctx, index)
+        instructions::add_validator_to_blacklist::handler(ctx, validator_history_blacklist)
     }
 
     /// Removes the validator at `index` from the blacklist
     pub fn remove_validator_from_blacklist(
         ctx: Context<RemoveValidatorFromBlacklist>,
-        index: u32,
+        validator_history_blacklist: u32,
     ) -> Result<()> {
-        instructions::remove_validator_from_blacklist::handler(ctx, index)
+        instructions::remove_validator_from_blacklist::handler(ctx, validator_history_blacklist)
     }
 
     /// For parameters that are present in args, the instruction checks that they are within sensible bounds and saves them to config struct
@@ -161,12 +186,27 @@ pub mod steward {
         instructions::update_parameters::handler(ctx, &update_parameters_args)
     }
 
-    /* Passthrough instructions to spl-stake-pool, where the signer is Staker. Must be invoked by `config.authority` */
+    /// Resets steward state account to its initial state.
+    pub fn reset_steward_state(ctx: Context<ResetStewardState>) -> Result<()> {
+        instructions::reset_steward_state::handler(ctx)
+    }
+
+    /// Closes Steward PDA accounts associated with a given Config (StewardStateAccount, and Staker).
+    /// Config is not closed as it is a Keypair, so lamports can simply be withdrawn.
+    /// Reclaims lamports to authority
+    pub fn close_steward_accounts(ctx: Context<CloseStewardAccounts>) -> Result<()> {
+        instructions::close_steward_accounts::handler(ctx)
+    }
+
+    /* Passthrough instructions */
+    /* passthrough to spl-stake-pool, where the signer is Staker. Must be invoked by `config.authority` */
 
+    /// Passthrough spl-stake-pool: Set the staker for the pool
     pub fn set_staker(ctx: Context<SetStaker>) -> Result<()> {
         instructions::spl_passthrough::set_staker_handler(ctx)
     }
 
+    /// Passthrough spl-stake-pool: Add a validator to the pool
     pub fn add_validator_to_pool(
         ctx: Context<AddValidatorToPool>,
         validator_seed: Option<u32>,
@@ -174,6 +214,7 @@ pub mod steward {
         instructions::spl_passthrough::add_validator_to_pool_handler(ctx, validator_seed)
     }
 
+    /// Passthrough spl-stake-pool: Remove a validator from the pool
     pub fn remove_validator_from_pool(
         ctx: Context<RemoveValidatorFromPool>,
         validator_list_index: u64,
@@ -184,6 +225,7 @@ pub mod steward {
         )
     }
 
+    /// Passthrough spl-stake-pool: Set the preferred validator
     pub fn set_preferred_validator(
         ctx: Context<SetPreferredValidator>,
         validator_type: PreferredValidatorType,
@@ -196,6 +238,7 @@ pub mod steward {
         )
     }
 
+    /// Passthrough spl-stake-pool: Increase validator stake
     pub fn increase_validator_stake(
         ctx: Context<IncreaseValidatorStake>,
         lamports: u64,
@@ -208,6 +251,7 @@ pub mod steward {
         )
     }
 
+    /// Passthrough spl-stake-pool: Decrease validator stake
     pub fn decrease_validator_stake(
         ctx: Context<DecreaseValidatorStake>,
         lamports: u64,
@@ -220,6 +264,7 @@ pub mod steward {
         )
     }
 
+    /// Passthrough spl-stake-pool: Increase additional validator stake
     pub fn increase_additional_validator_stake(
         ctx: Context<IncreaseAdditionalValidatorStake>,
         lamports: u64,
@@ -234,6 +279,7 @@ pub mod steward {
         )
     }
 
+    /// Passthrough spl-stake-pool: Decrease additional validator stake
     pub fn decrease_additional_validator_stake(
         ctx: Context<DecreaseAdditionalValidatorStake>,
         lamports: u64,
diff --git a/programs/steward/src/score.rs b/programs/steward/src/score.rs
index d0e04e2c..a82176de 100644
--- a/programs/steward/src/score.rs
+++ b/programs/steward/src/score.rs
@@ -51,7 +51,6 @@ pub struct ScoreComponents {
 
 pub fn validator_score(
     validator: &ValidatorHistory,
-    index: usize,
     cluster: &ClusterHistory,
     config: &Config,
     current_epoch: u16,
@@ -165,7 +164,7 @@ pub fn validator_score(
     /*
         If epoch credits exist, we expect the validator to have a superminority flag set. If not, scoring fails and we wait for
         the stake oracle to call UpdateStakeHistory.
-        If epoch credits is not set, we iterate through last 10 epochs to find the latest superminority flag.
+        If epoch credits is not set, we iterate through last `commission_range` epochs to find the latest superminority flag.
         If no entry is found, we assume the validator is not a superminority validator.
     */
     let is_superminority = if validator.history.epoch_credits_latest().is_some() {
@@ -195,7 +194,10 @@ pub fn validator_score(
     let superminority_score = if !is_superminority { 1.0 } else { 0.0 };
 
     /////// Blacklist ///////
-    let blacklisted_score = if config.blacklist.get(index).unwrap_or(false) {
+    let blacklisted_score = if config
+        .validator_history_blacklist
+        .get(validator.index as usize)?
+    {
         0.0
     } else {
         1.0
@@ -245,7 +247,7 @@ pub struct InstantUnstakeComponents {
     /// Checks if validator has increased MEV commission > mev_commission_bps_threshold
     pub mev_commission_check: bool,
 
-    /// Checks if validator was added to blacklist blacklisted
+    /// Checks if validator was added to blacklist
     pub is_blacklisted: bool,
 
     pub vote_account: Pubkey,
@@ -257,7 +259,6 @@ pub struct InstantUnstakeComponents {
 /// Before running, checks are needed on cluster and validator history to be updated this epoch past the halfway point of the epoch.
 pub fn instant_unstake_validator(
     validator: &ValidatorHistory,
-    index: usize,
     cluster: &ClusterHistory,
     config: &Config,
     epoch_start_slot: u64,
@@ -287,10 +288,7 @@ pub fn instant_unstake_validator(
         .checked_sub(epoch_start_slot)
         .ok_or(StewardError::ArithmeticError)?;
 
-    let vote_credits_rate = validator
-        .history
-        .epoch_credits_latest()
-        .ok_or(StewardError::VoteHistoryNotRecentEnough)? as f64
+    let vote_credits_rate = validator.history.epoch_credits_latest().unwrap_or(0) as f64
         / validator_history_slot_index as f64;
 
     let delinquency_check = if blocks_produced_rate > 0. {
@@ -324,7 +322,9 @@ pub fn instant_unstake_validator(
     let commission_check = commission > params.commission_threshold;
 
     /////// Blacklist ///////
-    let is_blacklisted = config.blacklist.get(index)?;
+    let is_blacklisted = config
+        .validator_history_blacklist
+        .get(validator.index as usize)?;
 
     let instant_unstake =
         delinquency_check || commission_check || mev_commission_check || is_blacklisted;
diff --git a/programs/steward/src/state/accounts.rs b/programs/steward/src/state/accounts.rs
index ee252adc..40460d65 100644
--- a/programs/steward/src/state/accounts.rs
+++ b/programs/steward/src/state/accounts.rs
@@ -4,7 +4,7 @@ use anchor_lang::prelude::*;
 use borsh::BorshSerialize;
 use type_layout::TypeLayout;
 
-use crate::{bitmask::BitMask, parameters::Parameters, utils::U8Bool, StewardState};
+use crate::{parameters::Parameters, utils::U8Bool, LargeBitMask, StewardState};
 
 /// Config is a user-provided keypair.
 /// This is so there can be multiple configs per stake pool, and one party can't
@@ -15,20 +15,37 @@ pub struct Config {
     /// SPL Stake Pool address that this program is managing
     pub stake_pool: Pubkey,
 
-    /// Authority for pool stewardship, can execute SPL Staker commands and adjust Delegation parameters
-    pub authority: Pubkey,
+    /// Validator List
+    pub validator_list: Pubkey,
+
+    /// Admin
+    /// - Update the `parameters_authority`
+    /// - Update the `blacklist_authority`
+    /// - Can call SPL Passthrough functions
+    /// - Can pause/reset the state machine
+    pub admin: Pubkey,
+
+    /// Parameters Authority
+    /// - Can update steward parameters
+    pub parameters_authority: Pubkey,
+
+    /// Blacklist Authority
+    /// - Can add to the blacklist
+    /// - Can remove from the blacklist
+    pub blacklist_authority: Pubkey,
 
     /// Bitmask representing index of validators that are not allowed delegation
-    pub blacklist: BitMask,
+    /// NOTE: This is indexed off of the validator history, NOT the validator list
+    pub validator_history_blacklist: LargeBitMask,
 
     /// Parameters for scoring, delegation, and state machine
     pub parameters: Parameters,
 
-    /// Padding for future governance parameters
-    pub _padding: [u8; 1023],
-
     /// Halts any state machine progress
     pub paused: U8Bool,
+
+    /// Padding for future governance parameters
+    pub _padding: [u8; 1023],
 }
 
 impl Config {
@@ -44,26 +61,6 @@ impl Config {
     }
 }
 
-// PDA that is used to sign instructions for the stake pool.
-// The pool's "staker" account needs to be assigned to this address,
-// and it has authority over adding validators, removing validators, and delegating stake.
-#[account]
-pub struct Staker {
-    pub bump: u8,
-}
-impl Staker {
-    pub const SIZE: usize = 8 + size_of::<Self>();
-    pub const SEED: &'static [u8] = b"staker";
-
-    pub fn get_address(config: &Pubkey) -> Pubkey {
-        let (pubkey, _) =
-            Pubkey::find_program_address(&[Self::SEED, config.as_ref()], &crate::id());
-        pubkey
-    }
-}
-
-// static_assertions::const_assert_eq!(StewardStateAccount::SIZE, 162584);
-
 #[derive(BorshSerialize)]
 #[account(zero_copy)]
 pub struct StewardStateAccount {
@@ -78,3 +75,10 @@ impl StewardStateAccount {
     pub const SEED: &'static [u8] = b"steward_state";
     pub const IS_INITIALIZED_BYTE_POSITION: usize = Self::SIZE - 8;
 }
+
+pub fn derive_steward_state_address(steward_config: &Pubkey) -> (Pubkey, u8) {
+    Pubkey::find_program_address(
+        &[StewardStateAccount::SEED, steward_config.as_ref()],
+        &crate::id(),
+    )
+}
diff --git a/programs/steward/src/state/large_bitmask.rs b/programs/steward/src/state/large_bitmask.rs
new file mode 100644
index 00000000..bfb96e78
--- /dev/null
+++ b/programs/steward/src/state/large_bitmask.rs
@@ -0,0 +1,104 @@
+/*
+    This file is largely copied over from bitmask.rs
+    This is because making a generic bitmask struct either didn't play well with
+    zero-copy, or it added too much overhead to a struct meant for performance.
+*/
+
+use anchor_lang::{prelude::Result, zero_copy};
+use borsh::{BorshDeserialize, BorshSerialize};
+
+use crate::errors::StewardError;
+
+//We are allocating at this size to handle future growth of ValidatorHistory accounts, at 2800 in June 2024
+const LARGE_BITMASK_INDEXES: usize = 20_000;
+
+#[allow(clippy::integer_division)]
+const LARGE_BITMASK: usize = (LARGE_BITMASK_INDEXES + 64 - 1) / 64; // ceil(LARGE_BITMASK_INDEXES / 64)
+
+/// Data structure used to efficiently pack a binary array, primarily used to store all validators.
+/// Each validator has an index (its index in the spl_stake_pool::ValidatorList), corresponding to a bit in the bitmask.
+/// When an operation is executed on a validator, the bit corresponding to that validator's index is set to 1.
+/// When all bits are 1, the operation is complete.
+#[derive(BorshSerialize, BorshDeserialize)]
+#[zero_copy]
+pub struct LargeBitMask {
+    pub values: [u64; LARGE_BITMASK],
+}
+
+impl Default for LargeBitMask {
+    fn default() -> Self {
+        Self {
+            values: [0; LARGE_BITMASK],
+        }
+    }
+}
+
+impl LargeBitMask {
+    #[allow(clippy::integer_division)]
+    pub fn set(&mut self, index: usize, value: bool) -> Result<()> {
+        if index >= LARGE_BITMASK_INDEXES {
+            return Err(StewardError::BitmaskOutOfBounds.into());
+        }
+        let word = index / 64;
+        let bit = index % 64;
+        if value {
+            self.values[word] |= 1 << bit;
+        } else {
+            self.values[word] &= !(1 << bit);
+        }
+        Ok(())
+    }
+
+    #[allow(clippy::integer_division)]
+    pub fn get(&self, index: usize) -> Result<bool> {
+        if index >= LARGE_BITMASK_INDEXES {
+            return Err(StewardError::BitmaskOutOfBounds.into());
+        }
+        let word = index / 64;
+        let bit = index % 64;
+        Ok((self.values[word] >> bit) & 1 == 1)
+    }
+
+    /// Unsafe version of get, which does not check if the index is out of bounds.
+    #[inline]
+    #[allow(clippy::integer_division, clippy::arithmetic_side_effects)]
+    pub const fn get_unsafe(&self, index: usize) -> bool {
+        let word = index / 64;
+        let bit = index % 64;
+        (self.values[word] >> bit) & 1 == 1
+    }
+
+    pub fn reset(&mut self) {
+        self.values = [0; LARGE_BITMASK];
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.values.iter().all(|&x| x == 0)
+    }
+
+    pub fn count(&self) -> usize {
+        self.values.iter().map(|x| x.count_ones() as usize).sum()
+    }
+
+    #[allow(clippy::integer_division)]
+    pub fn is_complete(&self, num_validators: u64) -> Result<bool> {
+        let num_validators = num_validators as usize;
+        if num_validators > LARGE_BITMASK_INDEXES {
+            return Err(StewardError::BitmaskOutOfBounds.into());
+        }
+        let full_words = num_validators / 64;
+        if !self.values[0..full_words].iter().all(|&x| x == u64::MAX) {
+            return Ok(false);
+        }
+        let remainder = num_validators % 64;
+        if remainder > 0 {
+            let mask: u64 = (1u64 << remainder)
+                .checked_sub(1)
+                .ok_or(StewardError::ArithmeticError)?;
+            if self.values[full_words] & mask != mask {
+                return Ok(false);
+            }
+        }
+        Ok(true)
+    }
+}
diff --git a/programs/steward/src/state/mod.rs b/programs/steward/src/state/mod.rs
index 6dbaa41f..9877fcfe 100644
--- a/programs/steward/src/state/mod.rs
+++ b/programs/steward/src/state/mod.rs
@@ -1,9 +1,11 @@
 pub mod accounts;
 pub mod bitmask;
+pub mod large_bitmask;
 pub mod parameters;
 pub mod steward_state;
 
 pub use accounts::*;
 pub use bitmask::*;
+pub use large_bitmask::*;
 pub use parameters::*;
 pub use steward_state::*;
diff --git a/programs/steward/src/state/parameters.rs b/programs/steward/src/state/parameters.rs
index 1f287358..0c74d515 100644
--- a/programs/steward/src/state/parameters.rs
+++ b/programs/steward/src/state/parameters.rs
@@ -174,7 +174,7 @@ pub struct Parameters {
 
     /// Required so that the struct is 8-byte aligned
     /// https://doc.rust-lang.org/reference/type-layout.html#reprc-structs
-    pub padding0: [u8; 6],
+    pub _padding_0: [u8; 6],
 
     /////// Delegation parameters ///////
     /// Number of validators to delegate to
@@ -207,6 +207,8 @@ pub struct Parameters {
 
     /// Minimum epochs voting required to be in the pool ValidatorList and eligible for delegation
     pub minimum_voting_epochs: u64,
+
+    pub _padding_1: [u64; 32],
 }
 
 impl Parameters {
diff --git a/programs/steward/src/state/steward_state.rs b/programs/steward/src/state/steward_state.rs
index e141a9ed..9ab234ac 100644
--- a/programs/steward/src/state/steward_state.rs
+++ b/programs/steward/src/state/steward_state.rs
@@ -5,12 +5,14 @@ use crate::{
     bitmask::BitMask,
     constants::{MAX_VALIDATORS, SORTED_INDEX_DEFAULT},
     delegation::{
-        decrease_stake_calculation, increase_stake_calculation, DecreaseComponents, RebalanceType,
-        UnstakeState,
+        decrease_stake_calculation, increase_stake_calculation, RebalanceType, UnstakeState,
     },
     errors::StewardError,
-    score::{instant_unstake_validator, validator_score},
-    utils::{epoch_progress, get_target_lamports, stake_lamports_at_validator_list_index, U8Bool},
+    events::{DecreaseComponents, StateTransition},
+    score::{
+        instant_unstake_validator, validator_score, InstantUnstakeComponents, ScoreComponents,
+    },
+    utils::{epoch_progress, get_target_lamports, stake_lamports_at_validator_list_index},
     Config, Parameters,
 };
 use anchor_lang::idl::types::*;
@@ -20,41 +22,29 @@ use bytemuck::{Pod, Zeroable};
 use spl_stake_pool::big_vec::BigVec;
 use validator_history::{ClusterHistory, ValidatorHistory};
 
-// Tests will fail here - comment out msg! to pass
-fn invalid_state_error(_expected: String, _actual: String) -> Error {
-    // msg!("Invalid state. Expected {}, Actual {}", expected, actual);
-    StewardError::InvalidState.into()
-}
-
-#[event]
-pub struct StateTransition {
-    epoch: u64,
-    slot: u64,
-    previous_state: String,
-    new_state: String,
-}
-
-pub fn maybe_transition_and_emit(
-    state_account: &mut StewardState,
+pub fn maybe_transition(
+    steward_state: &mut StewardState,
     clock: &Clock,
     params: &Parameters,
     epoch_schedule: &EpochSchedule,
-) -> Result<()> {
-    let initial_state = state_account.state_tag.to_string();
-    state_account.transition(clock, params, epoch_schedule)?;
-    if initial_state != state_account.state_tag.to_string() {
-        emit!(StateTransition {
+) -> Result<Option<StateTransition>> {
+    let initial_state = steward_state.state_tag.to_string();
+    steward_state.transition(clock, params, epoch_schedule)?;
+
+    if initial_state != steward_state.state_tag.to_string() {
+        return Ok(Some(StateTransition {
             epoch: clock.epoch,
             slot: clock.slot,
             previous_state: initial_state,
-            new_state: state_account.state_tag.to_string(),
-        });
+            new_state: steward_state.state_tag.to_string(),
+        }));
     }
-    Ok(())
+    Ok(None)
 }
 
 /// Tracks state of the stake pool.
-/// Follow state transitions here: [TODO add link to github diagram]
+/// Follow state transitions here:
+/// https://github.com/jito-foundation/stakenet/blob/master/programs/steward/state-machine-diagram.png
 #[derive(BorshSerialize)]
 #[zero_copy]
 pub struct StewardState {
@@ -87,6 +77,14 @@ pub struct StewardState {
     /// Tracks progress of states that require one instruction per validator
     pub progress: BitMask,
 
+    /// Marks a validator for immediate removal after `remove_validator_from_pool` has been called on the stake pool
+    /// This happens when a validator is able to be removed within the same epoch as it was marked
+    pub validators_for_immediate_removal: BitMask,
+
+    /// Marks a validator for removal after `remove_validator_from_pool` has been called on the stake pool
+    /// This is cleaned up in the next epoch
+    pub validators_to_remove: BitMask,
+
     ////// Cycle metadata fields //////
     /// Slot of the first ComputeScores instruction in the current cycle
     pub start_computing_scores_slot: u64,
@@ -110,18 +108,20 @@ pub struct StewardState {
     /// Total lamports that have been due to stake deposits this cycle
     pub stake_deposit_unstake_total: u64,
 
-    /// Tracks whether delegation computation has been completed
-    pub compute_delegations_completed: U8Bool,
+    /// Flags to track state transitions and operations
+    pub status_flags: u32,
 
-    /// Tracks whether unstake and delegate steps have completed
-    pub rebalance_completed: U8Bool,
+    /// Number of validators added to the pool in the current cycle
+    pub validators_added: u16,
 
     /// Future state and #[repr(C)] alignment
-    pub _padding0: [u8; 6 + MAX_VALIDATORS * 8],
+    pub _padding0: [u8; STATE_PADDING_0_SIZE],
     // TODO ADD MORE PADDING
 }
 
-#[derive(Clone, Copy)]
+pub const STATE_PADDING_0_SIZE: usize = MAX_VALIDATORS * 8 + 2;
+
+#[derive(Clone, Copy, PartialEq)]
 #[repr(u64)]
 pub enum StewardStateEnum {
     /// Start state
@@ -228,7 +228,40 @@ impl IdlBuild for StewardStateEnum {
     }
 }
 
+// BITS 0-7 COMPLETED PROGRESS FLAGS
+// Used to mark the completion of a particular state
+pub const COMPUTE_SCORE: u32 = 1 << 0;
+pub const COMPUTE_DELEGATIONS: u32 = 1 << 1;
+pub const EPOCH_MAINTENANCE: u32 = 1 << 2;
+pub const PRE_LOOP_IDLE: u32 = 1 << 3;
+pub const COMPUTE_INSTANT_UNSTAKES: u32 = 1 << 4;
+pub const REBALANCE: u32 = 1 << 5;
+pub const POST_LOOP_IDLE: u32 = 1 << 6;
+// BITS 8-15 RESERVED FOR FUTURE USE
+// BITS 16-23 OPERATIONAL FLAGS
+/// In epoch maintenance, when a new epoch is detected, we need a flag to tell the
+/// state transition layer that it needs to be reset to the IDLE state
+/// this flag is set in in epoch_maintenance and unset in the IDLE state transition
+pub const RESET_TO_IDLE: u32 = 1 << 16;
+// BITS 24-31 RESERVED FOR FUTURE USE
+
 impl StewardState {
+    pub fn set_flag(&mut self, flag: u32) {
+        self.status_flags |= flag;
+    }
+
+    pub fn clear_flags(&mut self) {
+        self.status_flags = 0;
+    }
+
+    pub fn unset_flag(&mut self, flag: u32) {
+        self.status_flags &= !flag;
+    }
+
+    pub fn has_flag(&self, flag: u32) -> bool {
+        self.status_flags & flag != 0
+    }
+
     /// Top level transition method. Tries to transition to a new state based on current state and epoch conditions
     pub fn transition(
         &mut self,
@@ -239,6 +272,7 @@ impl StewardState {
         let current_epoch = clock.epoch;
         let current_slot = clock.slot;
         let epoch_progress = epoch_progress(clock, epoch_schedule)?;
+
         match self.state_tag {
             StewardStateEnum::ComputeScores => self.transition_compute_scores(
                 current_epoch,
@@ -278,7 +312,6 @@ impl StewardState {
         num_epochs_between_scoring: u64,
     ) -> Result<()> {
         if current_epoch >= self.next_cycle_epoch {
-            self.state_tag = StewardStateEnum::ComputeScores;
             self.reset_state_for_new_cycle(
                 current_epoch,
                 current_slot,
@@ -288,6 +321,7 @@ impl StewardState {
             self.state_tag = StewardStateEnum::ComputeDelegations;
             self.progress = BitMask::default();
             self.delegations = [Delegation::default(); MAX_VALIDATORS];
+            self.set_flag(COMPUTE_SCORE);
         }
         Ok(())
     }
@@ -300,16 +334,13 @@ impl StewardState {
         num_epochs_between_scoring: u64,
     ) -> Result<()> {
         if current_epoch >= self.next_cycle_epoch {
-            self.state_tag = StewardStateEnum::ComputeScores;
             self.reset_state_for_new_cycle(
                 current_epoch,
                 current_slot,
                 num_epochs_between_scoring,
             )?;
-        } else if self.compute_delegations_completed.into() {
+        } else if self.has_flag(COMPUTE_DELEGATIONS) {
             self.state_tag = StewardStateEnum::Idle;
-            self.current_epoch = current_epoch;
-            self.rebalance_completed = false.into();
         }
         Ok(())
     }
@@ -323,20 +354,28 @@ impl StewardState {
         epoch_progress: f64,
         min_epoch_progress_for_instant_unstake: f64,
     ) -> Result<()> {
+        let completed_loop = self.has_flag(REBALANCE);
+
         if current_epoch >= self.next_cycle_epoch {
-            self.state_tag = StewardStateEnum::ComputeScores;
             self.reset_state_for_new_cycle(
                 current_epoch,
                 current_slot,
                 num_epochs_between_scoring,
             )?;
-        } else if (!self.rebalance_completed).into()
-            && epoch_progress >= min_epoch_progress_for_instant_unstake
-        {
-            self.state_tag = StewardStateEnum::ComputeInstantUnstake;
-            self.instant_unstake = BitMask::default();
-            self.progress = BitMask::default();
+        } else if !completed_loop {
+            self.unset_flag(RESET_TO_IDLE);
+
+            self.set_flag(PRE_LOOP_IDLE);
+
+            if epoch_progress >= min_epoch_progress_for_instant_unstake {
+                self.state_tag = StewardStateEnum::ComputeInstantUnstake;
+                self.instant_unstake = BitMask::default();
+                self.progress = BitMask::default();
+            }
+        } else if completed_loop {
+            self.set_flag(POST_LOOP_IDLE)
         }
+
         Ok(())
     }
 
@@ -348,20 +387,20 @@ impl StewardState {
         num_epochs_between_scoring: u64,
     ) -> Result<()> {
         if current_epoch >= self.next_cycle_epoch {
-            self.state_tag = StewardStateEnum::ComputeScores;
             self.reset_state_for_new_cycle(
                 current_epoch,
                 current_slot,
                 num_epochs_between_scoring,
             )?;
-        } else if current_epoch > self.current_epoch {
+        } else if self.has_flag(RESET_TO_IDLE) {
             self.state_tag = StewardStateEnum::Idle;
-            self.current_epoch = current_epoch;
             self.instant_unstake = BitMask::default();
             self.progress = BitMask::default();
+            // NOTE: RESET_TO_IDLE is cleared in the Idle transition
         } else if self.progress.is_complete(self.num_pool_validators)? {
             self.state_tag = StewardStateEnum::Rebalance;
             self.progress = BitMask::default();
+            self.set_flag(COMPUTE_INSTANT_UNSTAKES);
         }
         Ok(())
     }
@@ -374,21 +413,18 @@ impl StewardState {
         num_epochs_between_scoring: u64,
     ) -> Result<()> {
         if current_epoch >= self.next_cycle_epoch {
-            self.state_tag = StewardStateEnum::ComputeScores;
             self.reset_state_for_new_cycle(
                 current_epoch,
                 current_slot,
                 num_epochs_between_scoring,
             )?;
-        } else if current_epoch > self.current_epoch {
+        } else if self.has_flag(RESET_TO_IDLE) {
             self.state_tag = StewardStateEnum::Idle;
-            self.current_epoch = current_epoch;
             self.progress = BitMask::default();
-            self.rebalance_completed = false.into();
+            // NOTE: RESET_TO_IDLE is cleared in the Idle transition
         } else if self.progress.is_complete(self.num_pool_validators)? {
             self.state_tag = StewardStateEnum::Idle;
-            self.current_epoch = current_epoch;
-            self.rebalance_completed = true.into();
+            self.set_flag(REBALANCE);
         }
         Ok(())
     }
@@ -404,7 +440,6 @@ impl StewardState {
         self.scores = [0; MAX_VALIDATORS];
         self.yield_scores = [0; MAX_VALIDATORS];
         self.progress = BitMask::default();
-        self.current_epoch = current_epoch;
         self.next_cycle_epoch = current_epoch
             .checked_add(num_epochs_between_scoring)
             .ok_or(StewardError::ArithmeticError)?;
@@ -414,17 +449,39 @@ impl StewardState {
         self.stake_deposit_unstake_total = 0;
         self.delegations = [Delegation::default(); MAX_VALIDATORS];
         self.instant_unstake = BitMask::default();
-        self.compute_delegations_completed = false.into();
-        self.rebalance_completed = false.into();
+
+        let has_epoch_maintenance = self.has_flag(EPOCH_MAINTENANCE);
+        self.clear_flags();
+        if has_epoch_maintenance {
+            self.set_flag(EPOCH_MAINTENANCE);
+        }
+
         Ok(())
     }
 
     /// Update internal state when a validator is removed from the pool
     pub fn remove_validator(&mut self, index: usize) -> Result<()> {
-        self.num_pool_validators = self
-            .num_pool_validators
-            .checked_sub(1)
-            .ok_or(StewardError::ArithmeticError)?;
+        let marked_for_regular_removal = self.validators_to_remove.get(index)?;
+        let marked_for_immediate_removal = self.validators_for_immediate_removal.get(index)?;
+
+        require!(
+            marked_for_regular_removal || marked_for_immediate_removal,
+            StewardError::ValidatorNotMarkedForRemoval
+        );
+
+        // If the validator was marked for removal in the current cycle, decrement validators_added
+        if index >= self.num_pool_validators as usize {
+            self.validators_added = self
+                .validators_added
+                .checked_sub(1)
+                .ok_or(StewardError::ArithmeticError)?;
+        } else {
+            self.num_pool_validators = self
+                .num_pool_validators
+                .checked_sub(1)
+                .ok_or(StewardError::ArithmeticError)?;
+        }
+
         let num_pool_validators = self.num_pool_validators as usize;
 
         // Shift all validator state to the left
@@ -437,28 +494,34 @@ impl StewardState {
             self.instant_unstake
                 .set(i, self.instant_unstake.get(next_i)?)?;
             self.progress.set(i, self.progress.get(next_i)?)?;
+            self.validators_to_remove
+                .set(i, self.validators_to_remove.get(next_i)?)?;
+            self.validators_for_immediate_removal
+                .set(i, self.validators_for_immediate_removal.get(next_i)?)?;
         }
 
         // Update score indices
         let yield_score_index = self
             .sorted_yield_score_indices
             .iter()
-            .position(|&i| i == index as u16)
-            .ok_or(StewardError::ValidatorIndexOutOfBounds)?;
+            .position(|&i| i == index as u16);
         let score_index = self
             .sorted_score_indices
             .iter()
-            .position(|&i| i == index as u16)
-            .ok_or(StewardError::ValidatorIndexOutOfBounds)?;
+            .position(|&i| i == index as u16);
 
-        for i in yield_score_index..num_pool_validators {
-            let next_i = i.checked_add(1).ok_or(StewardError::ArithmeticError)?;
-            self.sorted_yield_score_indices[i] = self.sorted_yield_score_indices[next_i];
+        if let Some(yield_score_index) = yield_score_index {
+            for i in yield_score_index..num_pool_validators {
+                let next_i = i.checked_add(1).ok_or(StewardError::ArithmeticError)?;
+                self.sorted_yield_score_indices[i] = self.sorted_yield_score_indices[next_i];
+            }
         }
 
-        for i in score_index..num_pool_validators {
-            let next_i = i.checked_add(1).ok_or(StewardError::ArithmeticError)?;
-            self.sorted_score_indices[i] = self.sorted_score_indices[next_i];
+        if let Some(score_index) = score_index {
+            for i in score_index..num_pool_validators {
+                let next_i = i.checked_add(1).ok_or(StewardError::ArithmeticError)?;
+                self.sorted_score_indices[i] = self.sorted_score_indices[next_i];
+            }
         }
 
         for i in 0..num_pool_validators {
@@ -483,10 +546,34 @@ impl StewardState {
         self.delegations[num_pool_validators] = Delegation::default();
         self.instant_unstake.set(num_pool_validators, false)?;
         self.progress.set(num_pool_validators, false)?;
+        self.validators_to_remove.set(num_pool_validators, false)?;
+        self.validators_for_immediate_removal
+            .set(num_pool_validators, false)?;
 
         Ok(())
     }
 
+    /// Mark a validator for removal from the pool - this happens right after
+    /// `remove_validator_from_pool` has been called on the stake pool
+    /// This is cleaned up in the next epoch
+    pub fn mark_validator_for_removal(&mut self, index: usize) -> Result<()> {
+        self.validators_to_remove.set(index, true)
+    }
+
+    pub fn mark_validator_for_immediate_removal(&mut self, index: usize) -> Result<()> {
+        self.validators_for_immediate_removal.set(index, true)
+    }
+
+    /// Called when adding a validator to the pool so that we can ensure a 1-1 mapping between
+    /// the validator list and the steward state
+    pub fn increment_validator_to_add(&mut self) -> Result<()> {
+        self.validators_added = self
+            .validators_added
+            .checked_add(1)
+            .ok_or(StewardError::ArithmeticError)?;
+        Ok(())
+    }
+
     /// One instruction per validator. Can be done in any order.
     /// Computes score for a validator for the current epoch, stores score, and yield score component.
     /// Inserts this validator's index into sorted_score_indices and sorted_yield_score_indices, sorted by
@@ -503,27 +590,11 @@ impl StewardState {
         cluster: &ClusterHistory,
         config: &Config,
         num_pool_validators: u64,
-    ) -> Result<()> {
+    ) -> Result<Option<ScoreComponents>> {
         if matches!(self.state_tag, StewardStateEnum::ComputeScores) {
             let current_epoch = clock.epoch;
             let current_slot = clock.slot;
 
-            // Check that latest_update_slot is within the current epoch to guarantee previous epoch data is complete
-            let last_update_slot = validator
-                .history
-                .vote_account_last_update_slot_latest()
-                .ok_or(StewardError::VoteHistoryNotRecentEnough)?;
-            if last_update_slot < epoch_schedule.get_first_slot_in_epoch(current_epoch) {
-                return Err(StewardError::VoteHistoryNotRecentEnough.into());
-            }
-
-            // Check that cluster history is within current epoch to guarantee previous epoch data is complete
-            if cluster.cluster_history_last_update_slot
-                < epoch_schedule.get_first_slot_in_epoch(current_epoch)
-            {
-                return Err(StewardError::ClusterHistoryNotRecentEnough.into());
-            }
-
             /* Reset common state if:
                 - it's a new delegation cycle
                 - it's been more than `compute_score_slot_range` slots since compute scores started
@@ -542,11 +613,65 @@ impl StewardState {
                     config.parameters.num_epochs_between_scoring,
                 )?;
                 // Updates num_pool_validators at the start of the cycle so validator additions later won't be considered
+
+                require!(
+                    num_pool_validators == self.num_pool_validators + self.validators_added as u64,
+                    StewardError::ListStateMismatch
+                );
                 self.num_pool_validators = num_pool_validators;
+                self.validators_added = 0;
+            }
+
+            // Skip scoring if already processed
+            if self.progress.get(index)? {
+                return Ok(None);
+            }
+
+            // Skip scoring if marked for deletion
+            if self.validators_to_remove.get(index)?
+                || self.validators_for_immediate_removal.get(index)?
+            {
+                self.scores[index] = 0_u32;
+                self.yield_scores[index] = 0_u32;
+
+                let num_scores_calculated = self.progress.count();
+                insert_sorted_index(
+                    &mut self.sorted_score_indices,
+                    &self.scores,
+                    index as u16,
+                    self.scores[index],
+                    num_scores_calculated,
+                )?;
+                insert_sorted_index(
+                    &mut self.sorted_yield_score_indices,
+                    &self.yield_scores,
+                    index as u16,
+                    self.yield_scores[index],
+                    num_scores_calculated,
+                )?;
+
+                self.progress.set(index, true)?;
+
+                return Ok(None);
+            }
+
+            // Check that latest_update_slot is within the current epoch to guarantee previous epoch data is complete
+            let last_update_slot = validator
+                .history
+                .vote_account_last_update_slot_latest()
+                .ok_or(StewardError::VoteHistoryNotRecentEnough)?;
+            if last_update_slot < epoch_schedule.get_first_slot_in_epoch(current_epoch) {
+                return Err(StewardError::VoteHistoryNotRecentEnough.into());
+            }
+
+            // Check that cluster history is within current epoch to guarantee previous epoch data is complete
+            if cluster.cluster_history_last_update_slot
+                < epoch_schedule.get_first_slot_in_epoch(current_epoch)
+            {
+                return Err(StewardError::ClusterHistoryNotRecentEnough.into());
             }
 
-            let score = validator_score(validator, index, cluster, config, current_epoch as u16)?;
-            emit!(score);
+            let score = validator_score(validator, cluster, config, current_epoch as u16)?;
 
             self.scores[index] = (score.score * 1_000_000_000.) as u32;
             self.yield_scores[index] = (score.yield_score * 1_000_000_000.) as u32;
@@ -569,12 +694,10 @@ impl StewardState {
             )?;
 
             self.progress.set(index, true)?;
-            return Ok(());
+            return Ok(Some(score));
         }
-        Err(invalid_state_error(
-            "ComputeScores".to_string(),
-            self.state_tag.to_string(),
-        ))
+
+        Err(StewardError::InvalidState.into())
     }
 
     /// Given list of scores, finds top `num_delegation_validators` and assigns an equal share
@@ -584,10 +707,7 @@ impl StewardState {
     pub fn compute_delegations(&mut self, current_epoch: u64, config: &Config) -> Result<()> {
         if matches!(self.state_tag, StewardStateEnum::ComputeDelegations) {
             if current_epoch >= self.next_cycle_epoch {
-                return Err(invalid_state_error(
-                    "ComputeScores".to_string(),
-                    self.state_tag.to_string(),
-                ));
+                return Err(StewardError::InvalidState.into());
             }
 
             let validators_to_delegate = select_validators_to_delegate(
@@ -606,14 +726,11 @@ impl StewardState {
                 };
             }
 
-            self.compute_delegations_completed = true.into();
+            self.set_flag(COMPUTE_DELEGATIONS);
 
             return Ok(());
         }
-        Err(invalid_state_error(
-            "ComputeDelegations".to_string(),
-            self.state_tag.to_string(),
-        ))
+        Err(StewardError::InvalidState.into())
     }
 
     /// One instruction per validator.
@@ -629,13 +746,10 @@ impl StewardState {
         index: usize,
         cluster: &ClusterHistory,
         config: &Config,
-    ) -> Result<()> {
+    ) -> Result<Option<InstantUnstakeComponents>> {
         if matches!(self.state_tag, StewardStateEnum::ComputeInstantUnstake) {
             if clock.epoch >= self.next_cycle_epoch {
-                return Err(invalid_state_error(
-                    "ComputeScores".to_string(),
-                    self.state_tag.to_string(),
-                ));
+                return Err(StewardError::InvalidState.into());
             }
 
             if epoch_progress(clock, epoch_schedule)?
@@ -644,6 +758,19 @@ impl StewardState {
                 return Err(StewardError::InstantUnstakeNotReady.into());
             }
 
+            // Skip if already processed
+            if self.progress.get(index)? {
+                return Ok(None);
+            }
+
+            // Skip if marked for deletion
+            if self.validators_to_remove.get(index)?
+                || self.validators_for_immediate_removal.get(index)?
+            {
+                self.progress.set(index, true)?;
+                return Ok(None);
+            }
+
             let first_slot = epoch_schedule.get_first_slot_in_epoch(clock.epoch);
 
             // Epoch credits and cluster history must be updated in the current epoch and after the midpoint of the epoch
@@ -668,22 +795,18 @@ impl StewardState {
 
             let instant_unstake_result = instant_unstake_validator(
                 validator,
-                index,
                 cluster,
                 config,
                 first_slot,
                 clock.epoch as u16,
             )?;
-            emit!(instant_unstake_result);
+
             self.instant_unstake
                 .set(index, instant_unstake_result.instant_unstake)?;
             self.progress.set(index, true)?;
-            return Ok(());
+            return Ok(Some(instant_unstake_result));
         }
-        Err(invalid_state_error(
-            "ComputeInstantUnstake".to_string(),
-            self.state_tag.to_string(),
-        ))
+        Err(StewardError::InvalidState.into())
     }
 
     /// One instruction per validator.
@@ -708,23 +831,48 @@ impl StewardState {
     ) -> Result<RebalanceType> {
         if matches!(self.state_tag, StewardStateEnum::Rebalance) {
             if current_epoch >= self.next_cycle_epoch {
-                return Err(invalid_state_error(
-                    "ComputeScores".to_string(),
-                    self.state_tag.to_string(),
-                ));
+                return Err(StewardError::InvalidState.into());
             }
+
+            // Skip if already processed
+            if self.progress.get(index)? {
+                return Ok(RebalanceType::None);
+            }
+
+            // Skip if marked for deletion
+            if self.validators_to_remove.get(index)?
+                || self.validators_for_immediate_removal.get(index)?
+            {
+                self.progress.set(index, true)?;
+                return Ok(RebalanceType::None);
+            }
+
             let base_lamport_balance = minimum_delegation
                 .checked_add(stake_rent)
                 .ok_or(StewardError::ArithmeticError)?;
 
-            // Maximum increase amount is the total lamports in the reserve stake account minus 2 * stake_rent, which accounts for reserve rent + transient rent
-            // Saturating_sub because reserve stake may be less than 2 * stake_rent, but needs more than 2 * stake_rent to be able to delegate
-            let reserve_lamports = reserve_lamports.saturating_sub(
-                stake_rent
-                    .checked_mul(2)
-                    .ok_or(StewardError::ArithmeticError)?,
+            msg!("Reserve lamports before adjustment: {}", reserve_lamports);
+            msg!(
+                "Stake pool lamports before adjustment: {}",
+                stake_pool_lamports
             );
 
+            // Maximum increase amount is the total lamports in the reserve stake account minus (num_validators + 1) * stake_rent, which covers rent for all validators plus the transient rent
+            let all_accounts_needed_reserve_for_rent = validator_list
+                .len()
+                .checked_add(1)
+                .ok_or(StewardError::ArithmeticError)?;
+
+            let accounts_left_needed_reserve_for_rent = all_accounts_needed_reserve_for_rent
+                .checked_sub(self.progress.count() as u32)
+                .ok_or(StewardError::ArithmeticError)?;
+
+            let reserve_minimum = stake_rent
+                .checked_mul(accounts_left_needed_reserve_for_rent as u64)
+                .ok_or(StewardError::ArithmeticError)?;
+            // Saturating_sub because reserve stake may be less than the reserve_minimum but needs more than the reserve_minimum to be able to delegate
+            let reserve_lamports = reserve_lamports.saturating_sub(reserve_minimum);
+
             // Represents the amount of lamports that can be delegated to validators beyond the fixed costs of rent and minimum_delegation
             let stake_pool_lamports = stake_pool_lamports
                 .checked_sub(
@@ -810,6 +958,15 @@ impl StewardState {
                 RebalanceType::None
             };
 
+            msg!("Reserve lamports after adjustment: {}", reserve_lamports);
+            msg!(
+                "Stake pool lamports after adjustment: {}",
+                stake_pool_lamports
+            );
+            msg!("Rebalance Type: {:?}", rebalance);
+            msg!("Current Lamports: {}", current_lamports);
+            msg!("Target Lamports: {}", target_lamports);
+
             // Update internal state based on rebalance
             match rebalance {
                 RebalanceType::Decrease(DecreaseComponents {
@@ -818,13 +975,6 @@ impl StewardState {
                     stake_deposit_unstake_lamports,
                     total_unstake_lamports,
                 }) => {
-                    emit!(DecreaseComponents {
-                        scoring_unstake_lamports,
-                        instant_unstake_lamports,
-                        stake_deposit_unstake_lamports,
-                        total_unstake_lamports,
-                    });
-
                     self.validator_lamport_balances[index] = self.validator_lamport_balances[index]
                         .saturating_sub(total_unstake_lamports);
 
@@ -877,10 +1027,7 @@ impl StewardState {
             self.progress.set(index, true)?;
             return Ok(rebalance);
         }
-        Err(invalid_state_error(
-            "Rebalance".to_string(),
-            self.state_tag.to_string(),
-        ))
+        Err(StewardError::InvalidState.into())
     }
 }
 
diff --git a/programs/steward/src/utils.rs b/programs/steward/src/utils.rs
index 96f281fd..a991caa7 100644
--- a/programs/steward/src/utils.rs
+++ b/programs/steward/src/utils.rs
@@ -5,19 +5,139 @@ use borsh::{BorshDeserialize, BorshSerialize};
 use spl_pod::{bytemuck::pod_from_bytes, primitives::PodU64, solana_program::program_pack::Pack};
 use spl_stake_pool::{
     big_vec::BigVec,
-    state::{ValidatorListHeader, ValidatorStakeInfo},
+    state::{StakeStatus, ValidatorListHeader, ValidatorStakeInfo},
 };
 
-use crate::{errors::StewardError, Config, Delegation};
+use crate::{
+    constants::{STAKE_STATUS_OFFSET, U64_SIZE, VEC_SIZE_BYTES},
+    errors::StewardError,
+    Config, Delegation, StewardStateAccount, StewardStateEnum,
+};
+
+/// Checks called before any cranking state function. Note that expected_state is optional -
+/// this is due to ComputeScores handling it's own state check.
+pub fn state_checks(
+    clock: &Clock,
+    config: &Config,
+    state_account: &StewardStateAccount,
+    validator_list_account_info: &AccountInfo,
+    expected_state: Option<StewardStateEnum>,
+) -> Result<()> {
+    if config.is_paused() {
+        return Err(StewardError::StateMachinePaused.into());
+    }
+
+    if let Some(expected_state) = expected_state {
+        require!(
+            state_account.state.state_tag == expected_state,
+            StewardError::InvalidState
+        );
+    }
+
+    require!(
+        clock.epoch == state_account.state.current_epoch,
+        StewardError::EpochMaintenanceNotComplete
+    );
+
+    require!(
+        state_account.state.validators_for_immediate_removal.count() == 0,
+        StewardError::ValidatorsNeedToBeRemoved
+    );
+
+    // Ensure we have a 1-1 mapping between the number of validators
+    let validators_in_list = get_validator_list_length(validator_list_account_info)?;
+    require!(
+        state_account.state.num_pool_validators as usize
+            + state_account.state.validators_added as usize
+            == validators_in_list,
+        StewardError::ListStateMismatch
+    );
+
+    Ok(())
+}
+
+pub fn remove_validator_check(
+    clock: &Clock,
+    config: &Config,
+    state_account: &StewardStateAccount,
+    validator_list_account_info: &AccountInfo,
+) -> Result<()> {
+    if config.is_paused() {
+        return Err(StewardError::StateMachinePaused.into());
+    }
+
+    require!(
+        clock.epoch == state_account.state.current_epoch,
+        StewardError::EpochMaintenanceNotComplete
+    );
+
+    // Ensure we have a 1-1 mapping between the number of validators
+    let validators_in_list = get_validator_list_length(validator_list_account_info)?;
+    require!(
+        state_account.state.num_pool_validators as usize
+            + state_account.state.validators_added as usize
+            == validators_in_list,
+        StewardError::ListStateMismatch
+    );
+
+    Ok(())
+}
+
+pub fn add_validator_check(
+    clock: &Clock,
+    config: &Config,
+    state_account: &StewardStateAccount,
+    validator_list_account_info: &AccountInfo,
+) -> Result<()> {
+    if config.is_paused() {
+        return Err(StewardError::StateMachinePaused.into());
+    }
+
+    require!(
+        clock.epoch == state_account.state.current_epoch,
+        StewardError::EpochMaintenanceNotComplete
+    );
+
+    require!(
+        state_account.state.validators_for_immediate_removal.count() == 0,
+        StewardError::ValidatorsNeedToBeRemoved
+    );
+
+    // Ensure we have a 1-1 mapping between the number of validators
+    let validators_in_list = get_validator_list_length(validator_list_account_info)?;
+    require!(
+        state_account.state.num_pool_validators as usize
+            + state_account.state.validators_added as usize
+            == validators_in_list,
+        StewardError::ListStateMismatch
+    );
+
+    Ok(())
+}
 
 pub fn get_stake_pool_address(account: &AccountLoader<Config>) -> Result<Pubkey> {
     let config = account.load()?;
     Ok(config.stake_pool)
 }
 
-pub fn get_config_authority(account: &AccountLoader<Config>) -> Result<Pubkey> {
+pub fn get_validator_list(account: &AccountLoader<Config>) -> Result<Pubkey> {
+    let config = account.load()?;
+    Ok(config.validator_list)
+}
+
+pub fn get_config_admin(account: &AccountLoader<Config>) -> Result<Pubkey> {
+    let config = account.load()?;
+    Ok(config.admin)
+}
+
+pub fn get_config_blacklist_authority(account: &AccountLoader<Config>) -> Result<Pubkey> {
     let config = account.load()?;
-    Ok(config.authority)
+    Ok(config.blacklist_authority)
+}
+
+pub fn get_config_parameter_authority(account: &AccountLoader<Config>) -> Result<Pubkey> {
+    let config = account.load()?;
+    Ok(config.parameters_authority)
 }
 
 pub fn epoch_progress(clock: &Clock, epoch_schedule: &EpochSchedule) -> Result<f64> {
@@ -40,8 +160,6 @@ pub fn get_target_lamports(delegation: &Delegation, stake_pool_lamports: u64) ->
         .ok_or_else(|| StewardError::ArithmeticError.into())
 }
 
-const VEC_SIZE_BYTES: usize = 4;
-
 /// Utility to efficiently extract stake lamports and transient stake from a validator list.
 /// Frankenstein of spl_stake_pool::big_vec::BigVec::deserialize_slice
 /// and spl_stake_pool::state::ValidatorStakeInfo::active_lamports_greater_than
@@ -53,11 +171,11 @@ pub fn stake_lamports_at_validator_list_index(
     let active_start_index =
         VEC_SIZE_BYTES.saturating_add(index.saturating_mul(ValidatorStakeInfo::LEN));
     let active_end_index = active_start_index
-        .checked_add(8)
+        .checked_add(U64_SIZE)
         .ok_or(StewardError::ArithmeticError)?;
     let transient_start_index = active_end_index;
     let transient_end_index = transient_start_index
-        .checked_add(8)
+        .checked_add(U64_SIZE)
         .ok_or(StewardError::ArithmeticError)?;
     let slice = &validator_list.data[active_start_index..active_end_index];
     let active_stake_lamport_pod = pod_from_bytes::<PodU64>(slice).unwrap();
@@ -86,6 +204,50 @@ pub fn get_validator_stake_info_at_index(
     Ok(validator_stake_info)
 }
 
+pub fn check_validator_list_has_stake_status_other_than(
+    validator_list_account_info: &AccountInfo,
+    flags: &[StakeStatus],
+) -> Result<bool> {
+    let mut validator_list_data = validator_list_account_info.try_borrow_mut_data()?;
+    let (header, validator_list) = ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
+    require!(
+        header.account_type == spl_stake_pool::state::AccountType::ValidatorList,
+        StewardError::ValidatorListTypeMismatch
+    );
+
+    for index in 0..validator_list.len() as usize {
+        let stake_status_index = VEC_SIZE_BYTES
+            .saturating_add(index.saturating_mul(ValidatorStakeInfo::LEN))
+            .checked_add(STAKE_STATUS_OFFSET)
+            .ok_or(StewardError::ArithmeticError)?;
+
+        let stake_status = validator_list.data[stake_status_index];
+
+        let mut has_flag = false;
+        for flag in flags.iter() {
+            if stake_status == *flag as u8 {
+                has_flag = true;
+            }
+        }
+
+        if !has_flag {
+            return Ok(true);
+        }
+    }
+
+    Ok(false)
+}
+
+pub fn get_validator_list_length(validator_list_account_info: &AccountInfo) -> Result<usize> {
+    let mut validator_list_data = validator_list_account_info.try_borrow_mut_data()?;
+    let (header, validator_list) = ValidatorListHeader::deserialize_vec(&mut validator_list_data)?;
+    require!(
+        header.account_type == spl_stake_pool::state::AccountType::ValidatorList,
+        StewardError::ValidatorListTypeMismatch
+    );
+    Ok(validator_list.len() as usize)
+}
+
 /// A boolean type stored as a u8.
 #[derive(BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)]
 #[zero_copy]
@@ -184,6 +346,8 @@ impl IdlBuild for PreferredValidatorType {
     }
 }
 
+// Below are nice to haves for deserializing accounts but not strictly necessary for on-chain logic
+// A good amount of this is copied from anchor
 #[derive(Clone)]
 pub struct StakePool(spl_stake_pool::state::StakePool);
 
@@ -229,9 +393,6 @@ impl Deref for StakePool {
     }
 }
 
-// #[cfg(feature = "idl-build")]
-// impl anchor_lang::IdlBuild for StakePool {}
-
 #[derive(Clone)]
 pub struct ValidatorList(spl_stake_pool::state::ValidatorList);
 
diff --git a/programs/validator-history/Cargo.toml b/programs/validator-history/Cargo.toml
index 6ed95c8c..377f3cbb 100644
--- a/programs/validator-history/Cargo.toml
+++ b/programs/validator-history/Cargo.toml
@@ -17,6 +17,8 @@ name = "validator_history"
 no-entrypoint = []
 no-idl = []
 no-log-ix-name = []
+mainnet-beta = []
+testnet = []
 cpi = ["no-entrypoint"]
 default = ["custom-heap"]
 custom-heap = []
diff --git a/run_tests.sh b/run_tests.sh
index 4333e474..4a54f8e0 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -5,12 +5,12 @@ cargo build-sbf --manifest-path programs/steward/Cargo.toml;
 cargo build-sbf --manifest-path programs/validator-history/Cargo.toml;
 
 # Run all tests except the specified one
-SBF_OUT_DIR=$(pwd)/target/deploy RUST_MIN_STACK=5000000 cargo test -- --skip steward::test_state_methods
+SBF_OUT_DIR=$(pwd)/target/deploy RUST_MIN_STACK=8000000 cargo test -- --skip steward::test_state_methods
 
 # Check if the previous command succeeded
 if [ $? -eq 0 ]; then
     # Run the specific test in isolation
-    SBF_OUT_DIR=$(pwd)/target/deploy RUST_MIN_STACK=5000000 cargo test --package tests --test mod steward::test_state_methods
+    SBF_OUT_DIR=$(pwd)/target/deploy RUST_MIN_STACK=8000000 cargo test --package tests --test mod steward::test_state_methods
 else
     echo "Some tests failed, skipping the isolated test run."
 fi
\ No newline at end of file
diff --git a/tests/src/steward_fixtures.rs b/tests/src/steward_fixtures.rs
index f7a97489..65d53e61 100644
--- a/tests/src/steward_fixtures.rs
+++ b/tests/src/steward_fixtures.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::await_holding_refcell_ref)]
 use std::{cell::RefCell, rc::Rc, str::FromStr, vec};
 
 use crate::spl_stake_pool_cli;
@@ -14,8 +15,9 @@ use jito_steward::{
     bitmask::BitMask,
     constants::{MAX_VALIDATORS, SORTED_INDEX_DEFAULT, STAKE_POOL_WITHDRAW_SEED},
     utils::StakePool,
-    Config, Delegation, Parameters, Staker, StewardState, StewardStateAccount, StewardStateEnum,
-    UpdateParametersArgs,
+    utils::ValidatorList,
+    Config, Delegation, LargeBitMask, Parameters, StewardState, StewardStateAccount,
+    StewardStateEnum, UpdateParametersArgs, STATE_PADDING_0_SIZE,
 };
 use solana_program_test::*;
 use solana_sdk::{
@@ -24,8 +26,8 @@ use solana_sdk::{
     stake::state::StakeStateV2, transaction::Transaction,
 };
 use spl_stake_pool::{
-    find_stake_program_address, find_transient_stake_program_address,
-    state::{Fee, StakeStatus, ValidatorList, ValidatorStakeInfo},
+    find_stake_program_address, find_transient_stake_program_address, minimum_delegation,
+    state::{Fee, StakeStatus, ValidatorList as SPLValidatorList, ValidatorStakeInfo},
 };
 use validator_history::{
     self, constants::MAX_ALLOC_BYTES, CircBuf, CircBufCluster, ClusterHistory, ClusterHistoryEntry,
@@ -64,7 +66,6 @@ impl Default for StakePoolMetadata {
 pub struct TestFixture {
     pub ctx: Rc<RefCell<ProgramTestContext>>,
     pub stake_pool_meta: StakePoolMetadata,
-    pub staker: Pubkey,
     pub steward_config: Keypair,
     pub steward_state: Pubkey,
     pub cluster_history_account: Pubkey,
@@ -103,11 +104,6 @@ impl TestFixture {
 
         let stake_pool_meta = StakePoolMetadata::default();
         let steward_config = Keypair::new();
-        let staker = Pubkey::find_program_address(
-            &[Staker::SEED, steward_config.pubkey().as_ref()],
-            &jito_steward::id(),
-        )
-        .0;
         let steward_state = Pubkey::find_program_address(
             &[StewardStateAccount::SEED, steward_config.pubkey().as_ref()],
             &jito_steward::id(),
@@ -139,7 +135,6 @@ impl TestFixture {
         Self {
             ctx,
             stake_pool_meta,
-            staker,
             steward_state,
             steward_config,
             validator_history_config,
@@ -178,6 +173,30 @@ impl TestFixture {
         account
     }
 
+    pub async fn simulate_stake_pool_update(&self) {
+        let stake_pool: StakePool = self
+            .load_and_deserialize(&self.stake_pool_meta.stake_pool)
+            .await;
+
+        let mut stake_pool_spl = stake_pool.as_ref().clone();
+
+        let current_epoch = self
+            .ctx
+            .borrow_mut()
+            .banks_client
+            .get_sysvar::<Clock>()
+            .await
+            .unwrap()
+            .epoch;
+
+        stake_pool_spl.last_update_epoch = current_epoch;
+
+        self.ctx.borrow_mut().set_account(
+            &self.stake_pool_meta.stake_pool,
+            &serialized_stake_pool_account(stake_pool_spl, std::mem::size_of::<StakePool>()).into(),
+        );
+    }
+
     pub async fn initialize_stake_pool(&self) {
         // Call command_create_pool and execute transactions responded
         let mint = Keypair::new();
@@ -232,7 +251,7 @@ impl TestFixture {
         }
     }
 
-    pub async fn initialize_config(&self, parameters: Option<UpdateParametersArgs>) {
+    pub async fn initialize_steward(&self, parameters: Option<UpdateParametersArgs>) {
         // Default parameters from JIP
         let update_parameters_args = parameters.unwrap_or(UpdateParametersArgs {
             mev_commission_range: Some(0), // Set to pass validation, where epochs starts at 0
@@ -257,17 +276,16 @@ impl TestFixture {
 
         let instruction = Instruction {
             program_id: jito_steward::id(),
-            accounts: jito_steward::accounts::InitializeConfig {
+            accounts: jito_steward::accounts::InitializeSteward {
                 config: self.steward_config.pubkey(),
                 stake_pool: self.stake_pool_meta.stake_pool,
-                staker: self.staker,
+                state_account: self.steward_state,
                 stake_pool_program: spl_stake_pool::id(),
                 system_program: anchor_lang::solana_program::system_program::id(),
-                signer: self.keypair.pubkey(),
+                current_staker: self.keypair.pubkey(),
             }
             .to_account_metas(None),
-            data: jito_steward::instruction::InitializeConfig {
-                authority: self.keypair.pubkey(),
+            data: jito_steward::instruction::InitializeSteward {
                 update_parameters_args,
             }
             .data(),
@@ -282,23 +300,22 @@ impl TestFixture {
         self.submit_transaction_assert_success(transaction).await;
     }
 
-    pub async fn initialize_steward_state(&self) {
-        let instruction = Instruction {
-            program_id: jito_steward::id(),
-            accounts: jito_steward::accounts::InitializeState {
-                state_account: self.steward_state,
-                config: self.steward_config.pubkey(),
-                system_program: anchor_lang::solana_program::system_program::id(),
-                signer: self.keypair.pubkey(),
-            }
-            .to_account_metas(None),
-            data: jito_steward::instruction::InitializeState {}.data(),
+    pub async fn get_latest_blockhash(&self) -> Hash {
+        let blockhash = {
+            let mut banks_client = self.ctx.borrow_mut().banks_client.clone();
+            banks_client
+                .get_new_latest_blockhash(&Hash::default())
+                .await
+                .unwrap()
         };
 
-        let mut ixs = vec![instruction];
+        blockhash
+    }
 
+    pub async fn realloc_steward_state(&self) {
         // Realloc validator history account
         let mut num_reallocs = (StewardStateAccount::SIZE - MAX_ALLOC_BYTES) / MAX_ALLOC_BYTES + 1;
+        let mut ixs = vec![];
 
         while num_reallocs > 0 {
             ixs.extend(vec![
@@ -360,6 +377,39 @@ impl TestFixture {
         self.submit_transaction_assert_success(transaction).await;
     }
 
+    pub async fn initialize_validator_list(&self, num_validators: usize) {
+        let stake_program_minimum = self.fetch_minimum_delegation().await;
+        let pool_minimum_delegation = minimum_delegation(stake_program_minimum);
+        let stake_rent = self.fetch_stake_rent().await;
+        let minimum_active_stake_with_rent = pool_minimum_delegation + stake_rent;
+
+        let validator_list_account_info =
+            self.get_account(&self.stake_pool_meta.validator_list).await;
+
+        let validator_list: ValidatorList = self
+            .load_and_deserialize(&self.stake_pool_meta.validator_list)
+            .await;
+
+        let mut spl_validator_list = validator_list.as_ref().clone();
+
+        for _ in 0..num_validators {
+            spl_validator_list.validators.push(ValidatorStakeInfo {
+                active_stake_lamports: minimum_active_stake_with_rent.into(),
+                vote_account_address: Pubkey::new_unique(),
+                ..ValidatorStakeInfo::default()
+            });
+        }
+
+        self.ctx.borrow_mut().set_account(
+            &self.stake_pool_meta.validator_list,
+            &serialized_validator_list_account(
+                spl_validator_list.clone(),
+                Some(validator_list_account_info.data.len()),
+            )
+            .into(),
+        );
+    }
+
     // Turn this into a fixture creator
     pub async fn initialize_cluster_history_account(&self) -> ClusterHistory {
         todo!()
@@ -380,6 +430,7 @@ impl TestFixture {
             validator_history.history.push(ValidatorHistoryEntry {
                 epoch: i,
                 epoch_credits: 400000,
+                activated_stake_lamports: 100_000_000_000_000,
                 ..ValidatorHistoryEntry::default()
             });
         }
@@ -522,23 +573,15 @@ impl TestFixture {
         };
 
         if let Err(e) = process_transaction_result {
+            if !e.to_string().contains(error_message) {
+                panic!("Error: {}\n\nDoes not match {}", e, error_message);
+            }
+
             assert!(e.to_string().contains(error_message));
         } else {
             panic!("Error: Transaction succeeded. Expected {}", error_message);
         }
     }
-
-    pub async fn get_latest_blockhash(&self) -> Hash {
-        let blockhash = {
-            let mut banks_client = self.ctx.borrow_mut().banks_client.clone();
-            banks_client
-                .get_new_latest_blockhash(&Hash::default())
-                .await
-                .unwrap()
-        };
-
-        blockhash
-    }
 }
 
 pub fn validator_history_config_account(bump: u8, num_validators: u32) -> Account {
@@ -614,7 +657,7 @@ pub fn closed_vote_account() -> Account {
 
 // TODO write a function to serialize any account with T: AnchorSerialize
 pub fn serialized_validator_list_account(
-    validator_list: ValidatorList,
+    validator_list: SPLValidatorList,
     account_size: Option<usize>,
 ) -> Account {
     // Passes in size because zeros at the end will be truncated during serialization
@@ -796,7 +839,7 @@ impl Default for StateMachineFixtures {
             instant_unstake_delinquency_threshold_ratio: 0.1,
             commission_threshold: 10,
             historical_commission_threshold: 10,
-            padding0: [0; 6],
+            _padding_0: [0; 6],
             num_delegation_validators: 3,
             scoring_unstake_cap_bps: 1000,
             instant_unstake_cap_bps: 1000,
@@ -807,16 +850,20 @@ impl Default for StateMachineFixtures {
             num_epochs_between_scoring: 10,
             minimum_stake_lamports: 1,
             minimum_voting_epochs: 1,
+            _padding_1: [0; 32],
         };
 
         // Setup Config
         let config = Config {
             stake_pool: Pubkey::new_unique(),
-            authority: Pubkey::new_unique(),
-            blacklist: BitMask::default(),
             parameters,
-            _padding: [0; 1023],
             paused: false.into(),
+            validator_list: Pubkey::new_unique(),
+            admin: Pubkey::new_unique(),
+            parameters_authority: Pubkey::new_unique(),
+            blacklist_authority: Pubkey::new_unique(),
+            validator_history_blacklist: LargeBitMask::default(),
+            _padding: [0; 1023],
         };
 
         // Setup Sysvars: Clock, EpochSchedule
@@ -927,9 +974,11 @@ impl Default for StateMachineFixtures {
             stake_deposit_unstake_total: 0,
             delegations: [Delegation::default(); MAX_VALIDATORS],
             instant_unstake: BitMask::default(),
-            compute_delegations_completed: false.into(),
-            rebalance_completed: false.into(),
-            _padding0: [0; 6 + 8 * MAX_VALIDATORS],
+            status_flags: 0,
+            validators_added: 0,
+            validators_to_remove: BitMask::default(),
+            validators_for_immediate_removal: BitMask::default(),
+            _padding0: [0; STATE_PADDING_0_SIZE],
         };
 
         StateMachineFixtures {
diff --git a/tests/tests/steward/test_algorithms.rs b/tests/tests/steward/test_algorithms.rs
index 83e0b464..94a1545e 100644
--- a/tests/tests/steward/test_algorithms.rs
+++ b/tests/tests/steward/test_algorithms.rs
@@ -3,10 +3,10 @@ use anchor_lang::AnchorSerialize;
 use jito_steward::{
     constants::SORTED_INDEX_DEFAULT,
     delegation::{
-        decrease_stake_calculation, increase_stake_calculation, DecreaseComponents, RebalanceType,
-        UnstakeState,
+        decrease_stake_calculation, increase_stake_calculation, RebalanceType, UnstakeState,
     },
     errors::StewardError,
+    events::DecreaseComponents,
     insert_sorted_index,
     score::{
         instant_unstake_validator, validator_score, InstantUnstakeComponents, ScoreComponents,
@@ -36,7 +36,6 @@ fn test_compute_score() {
     // Regular run
     let components = validator_score(
         &good_validator,
-        good_validator.index as usize,
         &cluster_history,
         &config,
         current_epoch as u16,
@@ -64,14 +63,8 @@ fn test_compute_score() {
     let mut validator = good_validator;
     validator.history.last_mut().unwrap().mev_commission = 1001;
 
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -92,14 +85,8 @@ fn test_compute_score() {
 
     let mut validator = good_validator;
     validator.history.arr[11].mev_commission = 1001;
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -119,14 +106,8 @@ fn test_compute_score() {
     );
     let mut validator = good_validator;
     validator.history.arr[9].mev_commission = 1001;
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -148,17 +129,11 @@ fn test_compute_score() {
     // blacklist
     let validator = good_validator;
     config
-        .blacklist
+        .validator_history_blacklist
         .set(validator.index as usize, true)
         .unwrap();
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -176,19 +151,13 @@ fn test_compute_score() {
             epoch: current_epoch as u16
         }
     );
-    config.blacklist.reset();
+    config.validator_history_blacklist.reset();
 
     // superminority score
     let mut validator = good_validator;
     validator.history.last_mut().unwrap().is_superminority = 1;
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -212,14 +181,8 @@ fn test_compute_score() {
     for i in 0..19 {
         validator.history.arr_mut()[i].is_superminority = 1;
     }
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -244,14 +207,8 @@ fn test_compute_score() {
         validator.history.arr_mut()[i].mev_commission =
             ValidatorHistoryEntry::default().mev_commission;
     }
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -273,14 +230,8 @@ fn test_compute_score() {
     // commission
     let mut validator = good_validator;
     validator.history.last_mut().unwrap().commission = 11;
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -310,14 +261,8 @@ fn test_compute_score() {
     // commission above regular threshold, below historical threshold, outside of regular threshold window
     validator.history.arr[0].commission = 14;
 
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -337,14 +282,8 @@ fn test_compute_score() {
     );
 
     validator.history.arr[0].commission = 16;
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -371,14 +310,8 @@ fn test_compute_score() {
         validator.history.arr_mut()[i].epoch_credits = 880;
         cluster_history.history.arr_mut()[i].total_blocks = 1000;
     }
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -400,14 +333,8 @@ fn test_compute_score() {
     // delinquency
     let mut validator = good_validator;
     validator.history.arr[10].epoch_credits = 0;
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -434,14 +361,8 @@ fn test_compute_score() {
     cluster_history.history.arr[10].total_blocks = ClusterHistoryEntry::default().total_blocks;
     cluster_history.history.arr[11].total_blocks = ClusterHistoryEntry::default().total_blocks;
 
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -466,14 +387,8 @@ fn test_compute_score() {
     assert_eq!(current_epoch, 20);
     validator.history.arr[current_epoch as usize].epoch_credits = 0;
     cluster_history.history.arr[current_epoch as usize].total_blocks = 0;
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert_eq!(
         components,
         ScoreComponents {
@@ -505,27 +420,15 @@ fn test_compute_score() {
     validator.history.arr[current_epoch as usize - 2].is_superminority =
         ValidatorHistoryEntry::default().is_superminority;
     validator.history.arr[current_epoch as usize - 3].is_superminority = 1;
-    let components = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    )
-    .unwrap();
+    let components =
+        validator_score(&validator, &cluster_history, &config, current_epoch as u16).unwrap();
     assert!(components.superminority_score == 0.0);
 
     // Test error: superminority should exist if epoch credits exist
     let mut validator = good_validator;
     validator.history.arr[current_epoch as usize].is_superminority =
         ValidatorHistoryEntry::default().is_superminority;
-    let res = validator_score(
-        &validator,
-        validator.index as usize,
-        &cluster_history,
-        &config,
-        current_epoch as u16,
-    );
+    let res = validator_score(&validator, &cluster_history, &config, current_epoch as u16);
     assert!(res == Err(StewardError::StakeHistoryNotRecentEnough.into()));
 }
 
@@ -557,7 +460,6 @@ fn test_instant_unstake() {
 
     let res = instant_unstake_validator(
         &good_validator,
-        good_validator.index as usize,
         &cluster_history,
         &config,
         start_slot,
@@ -580,17 +482,17 @@ fn test_instant_unstake() {
 
     // Is blacklisted
     config
-        .blacklist
+        .validator_history_blacklist
         .set(good_validator.index as usize, true)
         .unwrap();
     let res = instant_unstake_validator(
         &good_validator,
-        good_validator.index as usize,
         &cluster_history,
         &config,
         start_slot,
         current_epoch,
     );
+
     assert!(res.is_ok());
     assert!(
         res.unwrap()
@@ -604,17 +506,17 @@ fn test_instant_unstake() {
                 epoch: current_epoch
             }
     );
-    config.blacklist.reset();
+    config.validator_history_blacklist.reset();
 
     // Delinquency threshold + Commission
     let res = instant_unstake_validator(
         &bad_validator,
-        bad_validator.index as usize,
         &cluster_history,
         &config,
         start_slot,
         current_epoch,
     );
+
     assert!(res.is_ok());
     assert!(
         res.unwrap()
@@ -635,12 +537,12 @@ fn test_instant_unstake() {
         ClusterHistoryEntry::default().total_blocks;
     let res = instant_unstake_validator(
         &bad_validator,
-        bad_validator.index as usize,
         &cluster_history,
         &config,
         start_slot,
         current_epoch,
     );
+
     assert!(res == Err(StewardError::ClusterHistoryNotRecentEnough.into()));
 
     let cluster_history = default_fixture.cluster_history;
@@ -650,13 +552,26 @@ fn test_instant_unstake() {
 
     let res = instant_unstake_validator(
         &validator,
-        validator.index as usize,
         &cluster_history,
         &config,
         start_slot,
         current_epoch,
     );
-    assert!(res == Err(StewardError::VoteHistoryNotRecentEnough.into()));
+    println!("NEED Error: {:?}", res);
+
+    assert!(res.is_ok());
+    assert!(
+        res.unwrap()
+            == InstantUnstakeComponents {
+                instant_unstake: true,
+                delinquency_check: true,
+                commission_check: false,
+                mev_commission_check: false,
+                is_blacklisted: false,
+                vote_account: validator.vote_account,
+                epoch: current_epoch
+            }
+    );
 
     let mut validator = validators[0];
     validator
@@ -667,12 +582,12 @@ fn test_instant_unstake() {
         ValidatorHistoryEntry::default().vote_account_last_update_slot;
     let res = instant_unstake_validator(
         &validator,
-        validator.index as usize,
         &cluster_history,
         &config,
         start_slot,
         current_epoch,
     );
+
     assert!(res == Err(StewardError::VoteHistoryNotRecentEnough.into()));
 
     // Not sure how commission would be unset with epoch credits set but test anyway
@@ -680,7 +595,6 @@ fn test_instant_unstake() {
     validator.history.last_mut().unwrap().commission = ValidatorHistoryEntry::default().commission;
     let res = instant_unstake_validator(
         &validator,
-        validator.index as usize,
         &cluster_history,
         &config,
         start_slot,
@@ -705,7 +619,6 @@ fn test_instant_unstake() {
         ValidatorHistoryEntry::default().mev_commission;
     let res = instant_unstake_validator(
         &validator,
-        validator.index as usize,
         &cluster_history,
         &config,
         start_slot,
@@ -730,7 +643,6 @@ fn test_instant_unstake() {
     cluster_history.history.last_mut().unwrap().total_blocks = 0;
     let res = instant_unstake_validator(
         &good_validator,
-        good_validator.index as usize,
         &cluster_history,
         &config,
         start_slot,
@@ -930,7 +842,7 @@ fn test_increase_stake_calculation() {
         0,
     );
     assert!(match result {
-        Err(e) => e == StewardError::InvalidState.into(),
+        Err(e) => e == StewardError::ValidatorIndexOutOfBounds.into(),
         _ => false,
     });
 
diff --git a/tests/tests/steward/test_integration.rs b/tests/tests/steward/test_integration.rs
index 8e158ef6..469f114d 100644
--- a/tests/tests/steward/test_integration.rs
+++ b/tests/tests/steward/test_integration.rs
@@ -34,10 +34,9 @@ use validator_history::{
 #[tokio::test]
 async fn test_compute_delegations() {
     let fixture = TestFixture::new().await;
-    let ctx = &fixture.ctx;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     let clock: Clock = fixture.get_sysvar().await;
 
@@ -96,12 +95,14 @@ async fn test_compute_delegations() {
         &serialized_config(steward_config).into(),
     );
 
+    fixture.initialize_validator_list(MAX_VALIDATORS).await;
+
     let compute_delegations_ix = Instruction {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::ComputeDelegations {
             config: fixture.steward_config.pubkey(),
             state_account: fixture.steward_state,
-            signer: fixture.keypair.pubkey(),
+            validator_list: fixture.stake_pool_meta.validator_list,
         }
         .to_account_metas(None),
         data: jito_steward::instruction::ComputeDelegations {}.data(),
@@ -113,8 +114,9 @@ async fn test_compute_delegations() {
         ],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
+
     fixture.submit_transaction_assert_success(tx).await;
 
     let steward_state_account: StewardStateAccount =
@@ -146,7 +148,7 @@ async fn test_compute_delegations() {
         accounts: jito_steward::accounts::ComputeDelegations {
             config: fixture.steward_config.pubkey(),
             state_account: fixture.steward_state,
-            signer: fixture.keypair.pubkey(),
+            validator_list: fixture.stake_pool_meta.validator_list,
         }
         .to_account_metas(None),
         data: jito_steward::instruction::ComputeDelegations {}.data(),
@@ -159,7 +161,7 @@ async fn test_compute_delegations() {
         ],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
     fixture
         .submit_transaction_assert_error(tx, "StateMachinePaused")
@@ -171,10 +173,9 @@ async fn test_compute_delegations() {
 #[tokio::test]
 async fn test_compute_scores() {
     let fixture = TestFixture::new().await;
-    let ctx = &fixture.ctx;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     let epoch_credits: Vec<(u64, u64, u64)> =
         vec![(0, 1, 0), (1, 2, 1), (2, 3, 2), (3, 4, 3), (4, 5, 4)];
@@ -198,6 +199,7 @@ async fn test_compute_scores() {
         validator_history.history.push(ValidatorHistoryEntry {
             epoch: i,
             epoch_credits: 1000,
+            activated_stake_lamports: 100_000_000_000_000,
             commission: 0,
             mev_commission: 0,
             is_superminority: 0,
@@ -245,6 +247,7 @@ async fn test_compute_scores() {
     steward_state_account.state.current_epoch = clock.epoch;
     steward_state_account.state.next_cycle_epoch =
         clock.epoch + steward_config.parameters.num_epochs_between_scoring;
+    // steward_state_account.state.validators_added = MAX_VALIDATORS as u16;
 
     // Setup validator list
     let mut validator_list_validators = (0..MAX_VALIDATORS)
@@ -284,6 +287,23 @@ async fn test_compute_scores() {
         &serialized_config(steward_config).into(),
     );
 
+    fixture.simulate_stake_pool_update().await;
+
+    let epoch_maintenance_ix = Instruction {
+        program_id: jito_steward::id(),
+        accounts: jito_steward::accounts::EpochMaintenance {
+            config: fixture.steward_config.pubkey(),
+            state_account: fixture.steward_state,
+            validator_list: fixture.stake_pool_meta.validator_list,
+            stake_pool: fixture.stake_pool_meta.stake_pool,
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::EpochMaintenance {
+            validator_index_to_remove: None,
+        }
+        .data(),
+    };
+
     // Basic test - test score computation that requires most compute
     let compute_scores_ix = Instruction {
         program_id: jito_steward::id(),
@@ -293,7 +313,6 @@ async fn test_compute_scores() {
             validator_history: validator_history_account,
             validator_list: fixture.stake_pool_meta.validator_list,
             cluster_history: cluster_history_account,
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::ComputeScore {
@@ -305,13 +324,14 @@ async fn test_compute_scores() {
     let tx = Transaction::new_signed_with_payer(
         &[
             // Only high because we are averaging 512 epochs
-            ComputeBudgetInstruction::set_compute_unit_limit(600_000),
+            ComputeBudgetInstruction::set_compute_unit_limit(800_000),
             ComputeBudgetInstruction::request_heap_frame(128 * 1024),
+            epoch_maintenance_ix.clone(),
             compute_scores_ix.clone(),
         ],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
 
     fixture.submit_transaction_assert_success(tx).await;
@@ -332,6 +352,30 @@ async fn test_compute_scores() {
 
     // Transition out of this state
     // Reset current state, set progress[1] to true, progress[0] to false
+
+    {
+        // Reset Validator List, such that there are only 2 validators
+        let mut validator_list_validators = (0..2)
+            .map(|_| ValidatorStakeInfo {
+                vote_account_address: Pubkey::new_unique(),
+                ..ValidatorStakeInfo::default()
+            })
+            .collect::<Vec<_>>();
+        validator_list_validators[0].vote_account_address = vote_account;
+        let validator_list = spl_stake_pool::state::ValidatorList {
+            header: ValidatorListHeader {
+                account_type: AccountType::ValidatorList,
+                max_validators: MAX_VALIDATORS as u32,
+            },
+            validators: validator_list_validators,
+        };
+
+        fixture.ctx.borrow_mut().set_account(
+            &fixture.stake_pool_meta.validator_list,
+            &serialized_validator_list_account(validator_list, None).into(),
+        );
+    }
+
     steward_state_account.state.num_pool_validators = 2;
     steward_state_account.state.scores[..2].copy_from_slice(&[0, 0]);
     steward_state_account.state.yield_scores[..2].copy_from_slice(&[0, 0]);
@@ -421,10 +465,9 @@ async fn test_compute_scores() {
 #[tokio::test]
 async fn test_compute_instant_unstake() {
     let fixture = TestFixture::new().await;
-    let ctx = &fixture.ctx;
     fixture.initialize_stake_pool().await;
     fixture
-        .initialize_config(Some(UpdateParametersArgs {
+        .initialize_steward(Some(UpdateParametersArgs {
             mev_commission_range: Some(0), // Set to pass validation, where epochs starts at 0
             epoch_credits_range: Some(0),  // Set to pass validation, where epochs starts at 0
             commission_range: Some(0),     // Set to pass validation, where epochs starts at 0
@@ -445,7 +488,7 @@ async fn test_compute_instant_unstake() {
             minimum_voting_epochs: Some(0), // Set to pass validation, where epochs starts at 0
         }))
         .await;
-    fixture.initialize_steward_state().await;
+    fixture.realloc_steward_state().await;
 
     let epoch_credits = vec![(0, 1, 0), (1, 2, 1), (2, 3, 2), (3, 4, 3), (4, 5, 4)];
     let vote_account = Pubkey::new_unique();
@@ -473,6 +516,7 @@ async fn test_compute_instant_unstake() {
     validator_history.history.push(ValidatorHistoryEntry {
         epoch: clock.epoch as u16,
         epoch_credits: 1000,
+        activated_stake_lamports: 100_000_000_000_000,
         commission: 100, // This is the condition causing instant unstake
         mev_commission: 0,
         is_superminority: 0,
@@ -535,10 +579,13 @@ async fn test_compute_instant_unstake() {
             account_type: AccountType::ValidatorList,
             max_validators: MAX_VALIDATORS as u32,
         },
-        validators: vec![ValidatorStakeInfo {
-            vote_account_address: vote_account,
-            ..ValidatorStakeInfo::default()
-        }],
+        validators: vec![
+            ValidatorStakeInfo {
+                vote_account_address: vote_account,
+                ..ValidatorStakeInfo::default()
+            },
+            ValidatorStakeInfo::default(),
+        ],
     };
 
     fixture.ctx.borrow_mut().set_account(
@@ -575,7 +622,6 @@ async fn test_compute_instant_unstake() {
             validator_history: validator_history_account,
             validator_list: fixture.stake_pool_meta.validator_list,
             cluster_history: cluster_history_account,
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::ComputeInstantUnstake {
@@ -588,7 +634,21 @@ async fn test_compute_instant_unstake() {
         &[compute_instant_unstake_ix.clone()],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
+    );
+
+    let test_state_account: StewardStateAccount =
+        fixture.load_and_deserialize(&fixture.steward_state).await;
+
+    let validator_list: ValidatorList = fixture
+        .load_and_deserialize(&fixture.stake_pool_meta.validator_list)
+        .await;
+
+    println!("{:?}", validator_list.validators.len());
+    println!(
+        "{:?}",
+        test_state_account.state.num_pool_validators
+            + test_state_account.state.validators_added as u64
     );
 
     fixture.submit_transaction_assert_success(tx).await;
@@ -658,8 +718,8 @@ async fn test_idle() {
     let fixture = TestFixture::new().await;
     let ctx = &fixture.ctx;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     let clock: Clock = fixture.get_sysvar().await;
     let epoch_schedule: EpochSchedule = fixture.get_sysvar().await;
@@ -679,6 +739,8 @@ async fn test_idle() {
     steward_state_account.state.current_epoch = epoch_schedule.first_normal_epoch;
     steward_state_account.state.num_pool_validators = MAX_VALIDATORS as u64;
 
+    fixture.initialize_validator_list(MAX_VALIDATORS).await;
+
     ctx.borrow_mut().set_account(
         &fixture.steward_state,
         &serialized_steward_state_account(steward_state_account).into(),
@@ -694,7 +756,7 @@ async fn test_idle() {
         accounts: jito_steward::accounts::Idle {
             config: fixture.steward_config.pubkey(),
             state_account: fixture.steward_state,
-            signer: fixture.keypair.pubkey(),
+            validator_list: fixture.stake_pool_meta.validator_list,
         }
         .to_account_metas(None),
         data: jito_steward::instruction::Idle {}.data(),
@@ -703,7 +765,7 @@ async fn test_idle() {
         &[idle_ix.clone()],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
 
     fixture.submit_transaction_assert_success(tx).await;
@@ -794,8 +856,8 @@ async fn test_rebalance_increase() {
         .advance_num_epochs(epoch_schedule.first_normal_epoch - clock.epoch, 10)
         .await;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     let mut steward_config: Config = fixture
         .load_and_deserialize(&fixture.steward_config.pubkey())
@@ -904,26 +966,17 @@ async fn test_rebalance_increase() {
         &serialized_stake_pool_account(stake_pool_spl, std::mem::size_of::<StakePool>()).into(),
     );
 
-    let mut steward_state_account: StewardStateAccount =
-        fixture.load_and_deserialize(&fixture.steward_state).await;
-
-    steward_state_account.state.num_pool_validators += 1;
-    ctx.borrow_mut().set_account(
-        &fixture.steward_state,
-        &serialized_steward_state_account(steward_state_account).into(),
-    );
-
     let (stake_account_address, transient_stake_account_address, withdraw_authority) =
         fixture.stake_accounts_for_validator(vote_account).await;
 
     let add_validator_to_pool_ix = Instruction {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::AutoAddValidator {
+            steward_state: fixture.steward_state,
             validator_history_account: validator_history_address,
             config: fixture.steward_config.pubkey(),
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             reserve_stake: fixture.stake_pool_meta.reserve,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
@@ -935,7 +988,6 @@ async fn test_rebalance_increase() {
             stake_config: stake::config::ID,
             stake_program: stake::program::id(),
             system_program: solana_program::system_program::id(),
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::AutoAddValidatorToPool {}.data(),
@@ -950,7 +1002,6 @@ async fn test_rebalance_increase() {
             stake_pool: fixture.stake_pool_meta.stake_pool,
             reserve_stake: fixture.stake_pool_meta.reserve,
             stake_pool_program: spl_stake_pool::id(),
-            staker: fixture.staker,
             withdraw_authority,
             vote_account,
             stake_account: stake_account_address,
@@ -961,7 +1012,6 @@ async fn test_rebalance_increase() {
             stake_program: stake::program::id(),
             stake_config: stake::config::ID,
             stake_history: solana_program::sysvar::stake_history::id(),
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::Rebalance {
@@ -978,11 +1028,22 @@ async fn test_rebalance_increase() {
         ],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
 
     fixture.submit_transaction_assert_success(tx).await;
 
+    let mut steward_state_account: StewardStateAccount =
+        fixture.load_and_deserialize(&fixture.steward_state).await;
+
+    // Force validator into the active set, don't wait for next cycle
+    steward_state_account.state.num_pool_validators += 1;
+    steward_state_account.state.validators_added -= 1;
+    ctx.borrow_mut().set_account(
+        &fixture.steward_state,
+        &serialized_steward_state_account(steward_state_account).into(),
+    );
+
     let reserve_before_rebalance = fixture.get_account(&fixture.stake_pool_meta.reserve).await;
 
     let tx = Transaction::new_signed_with_payer(
@@ -993,7 +1054,7 @@ async fn test_rebalance_increase() {
         ],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
 
     fixture.submit_transaction_assert_success(tx).await;
@@ -1013,7 +1074,13 @@ async fn test_rebalance_increase() {
         pool_minimum_delegation
     );
 
-    let expected_transient_stake = reserve_before_rebalance.lamports - 2 * stake_rent;
+    let steward_state_account: StewardStateAccount =
+        fixture.load_and_deserialize(&fixture.steward_state).await;
+
+    let validators_that_need_rent = steward_state_account.state.num_pool_validators + 1
+        - (steward_state_account.state.progress.count() as u64 - 1);
+    let expected_transient_stake =
+        reserve_before_rebalance.lamports - (stake_rent * validators_that_need_rent);
     assert_eq!(
         transient_stake_account.stake().unwrap().delegation.stake,
         expected_transient_stake
@@ -1032,8 +1099,8 @@ async fn test_rebalance_decrease() {
         .advance_num_epochs(epoch_schedule.first_normal_epoch - clock.epoch, 10)
         .await;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     let mut steward_config: Config = fixture
         .load_and_deserialize(&fixture.steward_config.pubkey())
@@ -1151,11 +1218,12 @@ async fn test_rebalance_decrease() {
     let add_validator_to_pool_ix = Instruction {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::AutoAddValidator {
+            steward_state: fixture.steward_state,
+
             validator_history_account: validator_history_address,
             config: fixture.steward_config.pubkey(),
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             reserve_stake: fixture.stake_pool_meta.reserve,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
@@ -1167,7 +1235,6 @@ async fn test_rebalance_decrease() {
             stake_config: stake::config::ID,
             stake_program: stake::program::id(),
             system_program: solana_program::system_program::id(),
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::AutoAddValidatorToPool {}.data(),
@@ -1181,10 +1248,21 @@ async fn test_rebalance_decrease() {
         ],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
     fixture.submit_transaction_assert_success(tx).await;
 
+    let mut steward_state_account: StewardStateAccount =
+        fixture.load_and_deserialize(&fixture.steward_state).await;
+
+    // Force validator into the active set, don't wait for next cycle
+    // steward_state_account.state.num_pool_validators += 1;
+    steward_state_account.state.validators_added -= 1;
+    ctx.borrow_mut().set_account(
+        &fixture.steward_state,
+        &serialized_steward_state_account(steward_state_account).into(),
+    );
+
     // Simulating stake deposit
     let stake_account_data = fixture.get_account(&stake_account_address).await;
 
@@ -1259,7 +1337,6 @@ async fn test_rebalance_decrease() {
             stake_pool: fixture.stake_pool_meta.stake_pool,
             reserve_stake: fixture.stake_pool_meta.reserve,
             stake_pool_program: spl_stake_pool::id(),
-            staker: fixture.staker,
             withdraw_authority,
             vote_account,
             stake_account: stake_account_address,
@@ -1270,7 +1347,6 @@ async fn test_rebalance_decrease() {
             stake_program: stake::program::id(),
             stake_config: stake::config::ID,
             stake_history: solana_program::sysvar::stake_history::id(),
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::Rebalance {
@@ -1287,7 +1363,7 @@ async fn test_rebalance_decrease() {
         ],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
     fixture.submit_transaction_assert_success(tx).await;
 
@@ -1334,13 +1410,12 @@ async fn test_rebalance_other_cases() {
         .advance_num_epochs(epoch_schedule.first_normal_epoch - clock.epoch, 10)
         .await;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     let mut steward_config: Config = fixture
         .load_and_deserialize(&fixture.steward_config.pubkey())
         .await;
-    steward_config.set_paused(true);
     steward_config.parameters.minimum_voting_epochs = 1;
 
     let vote_account = Pubkey::new_unique();
@@ -1372,6 +1447,28 @@ async fn test_rebalance_other_cases() {
         });
     }
 
+    let mut steward_state_account: StewardStateAccount =
+        fixture.load_and_deserialize(&fixture.steward_state).await;
+    let clock: Clock = fixture.get_sysvar().await;
+
+    steward_state_account.state.current_epoch = clock.epoch;
+    steward_state_account.state.num_pool_validators = MAX_VALIDATORS as u64 - 1;
+    steward_state_account.state.state_tag = StewardStateEnum::Rebalance;
+
+    ctx.borrow_mut().set_account(
+        &fixture.steward_state,
+        &serialized_steward_state_account(steward_state_account).into(),
+    );
+
+    ctx.borrow_mut().set_account(
+        &fixture.stake_pool_meta.validator_list,
+        &serialized_validator_list_account(
+            spl_validator_list.clone(),
+            Some(validator_list_account_info.data.len()),
+        )
+        .into(),
+    );
+
     ctx.borrow_mut().set_account(
         &fixture.stake_pool_meta.validator_list,
         &serialized_validator_list_account(
@@ -1392,11 +1489,11 @@ async fn test_rebalance_other_cases() {
     let add_validator_to_pool_ix = Instruction {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::AutoAddValidator {
+            steward_state: fixture.steward_state,
             validator_history_account: validator_history_address,
             config: fixture.steward_config.pubkey(),
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             reserve_stake: fixture.stake_pool_meta.reserve,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
@@ -1408,7 +1505,6 @@ async fn test_rebalance_other_cases() {
             stake_config: stake::config::ID,
             stake_program: stake::program::id(),
             system_program: solana_program::system_program::id(),
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::AutoAddValidatorToPool {}.data(),
@@ -1422,10 +1518,21 @@ async fn test_rebalance_other_cases() {
         ],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
     fixture.submit_transaction_assert_success(tx).await;
 
+    let mut steward_state_account: StewardStateAccount =
+        fixture.load_and_deserialize(&fixture.steward_state).await;
+
+    // Force validator into the active set, don't wait for next cycle
+    steward_state_account.state.num_pool_validators += 1;
+    steward_state_account.state.validators_added -= 1;
+    ctx.borrow_mut().set_account(
+        &fixture.steward_state,
+        &serialized_steward_state_account(steward_state_account).into(),
+    );
+
     let rebalance_ix = Instruction {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::Rebalance {
@@ -1436,7 +1543,6 @@ async fn test_rebalance_other_cases() {
             stake_pool: fixture.stake_pool_meta.stake_pool,
             reserve_stake: fixture.stake_pool_meta.reserve,
             stake_pool_program: spl_stake_pool::id(),
-            staker: fixture.staker,
             withdraw_authority,
             vote_account,
             stake_account: stake_account_address,
@@ -1447,7 +1553,6 @@ async fn test_rebalance_other_cases() {
             stake_program: stake::program::id(),
             stake_config: stake::config::ID,
             stake_history: solana_program::sysvar::stake_history::id(),
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::Rebalance {
@@ -1459,8 +1564,19 @@ async fn test_rebalance_other_cases() {
         &[rebalance_ix.clone()],
         Some(&fixture.keypair.pubkey()),
         &[&fixture.keypair],
-        ctx.borrow().last_blockhash,
+        fixture.get_latest_blockhash().await,
     );
+
+    let mut steward_config: Config = fixture
+        .load_and_deserialize(&fixture.steward_config.pubkey())
+        .await;
+    steward_config.set_paused(true);
+
+    ctx.borrow_mut().set_account(
+        &fixture.steward_config.pubkey(),
+        &serialized_config(steward_config).into(),
+    );
+
     fixture
         .submit_transaction_assert_error(tx, "StateMachinePaused")
         .await;
diff --git a/tests/tests/steward/test_parameters.rs b/tests/tests/steward/test_parameters.rs
index 591dce04..36c7a08e 100644
--- a/tests/tests/steward/test_parameters.rs
+++ b/tests/tests/steward/test_parameters.rs
@@ -174,8 +174,8 @@ async fn _set_parameter(fixture: &TestFixture, update_parameters: &UpdateParamet
 async fn test_update_parameters() {
     let fixture = TestFixture::new().await;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     _set_parameter(
         &fixture,
@@ -242,7 +242,8 @@ fn _test_parameter(
         num_epochs_between_scoring: 10,
         minimum_stake_lamports: 5_000_000_000_000,
         minimum_voting_epochs: 5,
-        padding0: [0; 6],
+        _padding_0: [0; 6],
+        _padding_1: [0; 32],
     });
 
     // First Valid Epoch
diff --git a/tests/tests/steward/test_spl_passthrough.rs b/tests/tests/steward/test_spl_passthrough.rs
index a3448572..141bb451 100644
--- a/tests/tests/steward/test_spl_passthrough.rs
+++ b/tests/tests/steward/test_spl_passthrough.rs
@@ -6,8 +6,9 @@ use anchor_lang::{
 };
 use jito_steward::{
     constants::MAX_VALIDATORS,
+    derive_steward_state_address,
     utils::{StakePool, ValidatorList},
-    Config, Delegation, Staker, StewardStateAccount, StewardStateEnum,
+    Config, Delegation, StewardStateAccount, StewardStateEnum,
 };
 use rand::prelude::SliceRandom;
 use rand::{rngs::StdRng, SeedableRng};
@@ -158,14 +159,16 @@ async fn _add_test_validator(fixture: &TestFixture, vote_account: Pubkey) {
     let (stake_account_address, _, withdraw_authority) =
         fixture.stake_accounts_for_validator(vote_account).await;
 
+    fixture.simulate_stake_pool_update().await;
+
     // Add Validator
     let instruction = Instruction {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::AddValidatorToPool {
+            state_account: fixture.steward_state,
             config: fixture.steward_config.pubkey(),
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             reserve_stake: fixture.stake_pool_meta.reserve,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
@@ -177,7 +180,7 @@ async fn _add_test_validator(fixture: &TestFixture, vote_account: Pubkey) {
             stake_config: stake::config::ID,
             system_program: system_program::id(),
             stake_program: stake::program::id(),
-            signer: fixture.keypair.pubkey(),
+            admin: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::AddValidatorToPool {
@@ -224,11 +227,11 @@ async fn _set_and_check_preferred_validator(
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::SetPreferredValidator {
             config: fixture.steward_config.pubkey(),
+            state_account: fixture.steward_state,
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             validator_list: fixture.stake_pool_meta.validator_list,
-            signer: fixture.keypair.pubkey(),
+            admin: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::SetPreferredValidator {
@@ -307,11 +310,10 @@ async fn _increase_and_check_stake(
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::IncreaseValidatorStake {
             config: fixture.steward_config.pubkey(),
-            steward_state: fixture.steward_state,
+            state_account: fixture.steward_state,
             validator_history,
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
             reserve_stake: fixture.stake_pool_meta.reserve,
@@ -324,7 +326,7 @@ async fn _increase_and_check_stake(
             stake_config: stake::config::ID,
             system_program: system_program::id(),
             stake_program: stake::program::id(),
-            signer: fixture.keypair.pubkey(),
+            admin: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::IncreaseValidatorStake {
@@ -400,11 +402,10 @@ async fn _increase_and_check_additional_stake(
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::IncreaseAdditionalValidatorStake {
             config: fixture.steward_config.pubkey(),
-            steward_state: fixture.steward_state,
+            state_account: fixture.steward_state,
             validator_history,
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
             reserve_stake: fixture.stake_pool_meta.reserve,
@@ -416,7 +417,7 @@ async fn _increase_and_check_additional_stake(
             stake_config: stake::config::ID,
             system_program: system_program::id(),
             stake_program: stake::program::id(),
-            signer: fixture.keypair.pubkey(),
+            admin: fixture.keypair.pubkey(),
             ephemeral_stake_account,
         }
         .to_account_metas(None),
@@ -452,16 +453,16 @@ async fn _increase_and_check_additional_stake(
     );
 }
 
-pub async fn _set_staker(fixture: &TestFixture, staker: Pubkey, new_staker: Pubkey) {
+pub async fn _set_staker(fixture: &TestFixture, new_staker: Pubkey) {
     let instruction = Instruction {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::SetStaker {
             config: fixture.steward_config.pubkey(),
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker,
+            state_account: fixture.steward_state,
             new_staker,
-            signer: fixture.keypair.pubkey(),
+            admin: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::SetStaker {}.data(),
@@ -491,8 +492,8 @@ async fn test_add_validator_to_pool() {
     // Set up the test fixture
     let fixture = TestFixture::new().await;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     {
         // Test add 1 validator
@@ -500,7 +501,7 @@ async fn test_add_validator_to_pool() {
     }
 
     {
-        // Add 5 validators
+        // Add 10 validators
         for _ in 0..10 {
             _add_test_validator(&fixture, Pubkey::new_unique()).await;
         }
@@ -514,11 +515,11 @@ async fn test_remove_validator_from_pool() {
     // Set up the test fixture
     let fixture = TestFixture::new().await;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     // Setup the steward state
-    _setup_test_steward_state(&fixture, MAX_VALIDATORS, 1_000_000_000).await;
+    // _setup_test_steward_state(&fixture, MAX_VALIDATORS, 1_000_000_000).await;
 
     // Assert the validator was added to the validator list
     _add_test_validator(&fixture, Pubkey::new_unique()).await;
@@ -544,10 +545,9 @@ async fn test_remove_validator_from_pool() {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::RemoveValidatorFromPool {
             config: fixture.steward_config.pubkey(),
-            steward_state: fixture.steward_state,
+            state_account: fixture.steward_state,
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
             stake_account: stake_account_address,
@@ -555,7 +555,7 @@ async fn test_remove_validator_from_pool() {
             clock: sysvar::clock::id(),
             system_program: system_program::id(),
             stake_program: stake::program::id(),
-            signer: fixture.keypair.pubkey(),
+            admin: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::RemoveValidatorFromPool {
@@ -609,8 +609,8 @@ async fn test_set_preferred_validator() {
     // Set up the test fixture
     let fixture = TestFixture::new().await;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     // Assert the validator was added to the validator list
     _add_test_validator(&fixture, Pubkey::new_unique()).await;
@@ -667,8 +667,8 @@ async fn test_increase_validator_stake() {
     let fixture = TestFixture::new().await;
 
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     // Assert the validator was added to the validator list
     _add_test_validator(&fixture, Pubkey::new_unique()).await;
@@ -689,8 +689,8 @@ async fn test_decrease_validator_stake() {
     let fixture = TestFixture::new().await;
 
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     _add_test_validator(&fixture, Pubkey::new_unique()).await;
 
@@ -724,11 +724,10 @@ async fn test_decrease_validator_stake() {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::DecreaseValidatorStake {
             config: fixture.steward_config.pubkey(),
-            steward_state: fixture.steward_state,
+            state_account: fixture.steward_state,
             validator_history,
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
             reserve_stake: fixture.stake_pool_meta.reserve,
@@ -740,7 +739,7 @@ async fn test_decrease_validator_stake() {
             stake_history: sysvar::stake_history::id(),
             system_program: system_program::id(),
             stake_program: stake::program::id(),
-            signer: fixture.keypair.pubkey(),
+            admin: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::DecreaseValidatorStake {
@@ -769,8 +768,8 @@ async fn test_increase_additional_validator_stake() {
     let fixture = TestFixture::new().await;
 
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     // Assert the validator was added to the validator list
     _add_test_validator(&fixture, Pubkey::new_unique()).await;
@@ -792,8 +791,8 @@ async fn test_decrease_additional_validator_stake() {
     let fixture = TestFixture::new().await;
 
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     _add_test_validator(&fixture, Pubkey::new_unique()).await;
 
@@ -805,6 +804,7 @@ async fn test_decrease_additional_validator_stake() {
     let validator_list_account_raw = fixture
         .get_account(&fixture.stake_pool_meta.validator_list)
         .await;
+
     let validator_list_account: ValidatorList =
         ValidatorList::try_deserialize_unchecked(&mut validator_list_account_raw.data.as_slice())
             .expect("Failed to deserialize validator list account");
@@ -814,14 +814,20 @@ async fn test_decrease_additional_validator_stake() {
         .get(validator_list_index)
         .expect("Validator is not in list");
 
+    println!("4");
+
     let vote_account = validator_to_increase_stake.vote_account_address;
     let (stake_account_address, transient_stake_account_address, withdraw_authority) =
         fixture.stake_accounts_for_validator(vote_account).await;
 
+    println!("5");
+
     _simulate_stake_deposit(&fixture, stake_account_address, 2_000_000_000).await;
+    println!("6");
 
     let validator_history =
         fixture.initialize_validator_history_with_credits(vote_account, validator_list_index);
+    println!("7");
 
     let (ephemeral_stake_account, _) = find_ephemeral_stake_program_address(
         &spl_stake_pool::id(),
@@ -833,11 +839,10 @@ async fn test_decrease_additional_validator_stake() {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::DecreaseAdditionalValidatorStake {
             config: fixture.steward_config.pubkey(),
-            steward_state: fixture.steward_state,
+            state_account: fixture.steward_state,
             validator_history,
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
             reserve_stake: fixture.stake_pool_meta.reserve,
@@ -848,7 +853,7 @@ async fn test_decrease_additional_validator_stake() {
             stake_history: sysvar::stake_history::id(),
             system_program: system_program::id(),
             stake_program: stake::program::id(),
-            signer: fixture.keypair.pubkey(),
+            admin: fixture.keypair.pubkey(),
             ephemeral_stake_account,
         }
         .to_account_metas(None),
@@ -861,6 +866,7 @@ async fn test_decrease_additional_validator_stake() {
     };
 
     let latest_blockhash = _get_latest_blockhash(&fixture).await;
+    println!("8");
 
     let transaction = Transaction::new_signed_with_payer(
         &[instruction],
@@ -869,6 +875,7 @@ async fn test_decrease_additional_validator_stake() {
         latest_blockhash,
     );
     fixture.submit_transaction_assert_success(transaction).await;
+    println!("9");
 
     drop(fixture);
 }
@@ -878,8 +885,8 @@ async fn test_set_staker() {
     // Set up the test fixture
     let fixture = TestFixture::new().await;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     let new_staker = Keypair::new();
 
@@ -894,15 +901,12 @@ async fn test_set_staker() {
             StakePool::try_deserialize_unchecked(&mut stake_pool_account_raw.data.as_slice())
                 .expect("Failed to deserialize stake pool account");
 
-        let (staker, _) = Pubkey::find_program_address(
-            &[Staker::SEED, fixture.steward_config.pubkey().as_ref()],
-            &jito_steward::id(),
-        );
+        let (steward_state, _) = derive_steward_state_address(&fixture.steward_config.pubkey());
 
         // Assert accounts are set up correctly
-        assert!(stake_pool_account.staker.eq(&staker));
-        assert!(fixture.staker.eq(&staker));
-        assert!(config_account.authority.eq(&fixture.keypair.pubkey()));
+        assert!(stake_pool_account.staker.eq(&steward_state));
+        assert!(fixture.steward_state.eq(&steward_state));
+        assert!(config_account.admin.eq(&fixture.keypair.pubkey()));
         assert!(config_account
             .stake_pool
             .eq(&fixture.stake_pool_meta.stake_pool));
@@ -910,12 +914,12 @@ async fn test_set_staker() {
 
     {
         // Test 1: Set staker to same staker
-        _set_staker(&fixture, fixture.staker, fixture.staker).await;
+        _set_staker(&fixture, fixture.steward_state).await;
     }
 
     {
         // Test 2: Set staker to different staker
-        _set_staker(&fixture, fixture.staker, new_staker.pubkey()).await;
+        _set_staker(&fixture, new_staker.pubkey()).await;
     }
 
     drop(fixture);
diff --git a/tests/tests/steward/test_state_methods.rs b/tests/tests/steward/test_state_methods.rs
index 6ec24a1e..7bab19ff 100644
--- a/tests/tests/steward/test_state_methods.rs
+++ b/tests/tests/steward/test_state_methods.rs
@@ -71,6 +71,7 @@ fn test_compute_scores() {
     assert!(state.current_epoch == current_epoch);
 
     // Test invalid state
+    state.progress.reset();
     state.state_tag = StewardStateEnum::Idle;
     let res = state.compute_score(
         clock,
@@ -150,7 +151,7 @@ fn test_compute_scores() {
 
     // Test blacklist validator
     config
-        .blacklist
+        .validator_history_blacklist
         .set(validators[0].index as usize, true)
         .unwrap();
     let res = state.compute_score(
@@ -187,25 +188,26 @@ fn test_compute_scores() {
     assert!(res.is_ok());
     assert!(state.start_computing_scores_slot == clock.slot);
     assert!(state.next_cycle_epoch == current_epoch + parameters.num_epochs_between_scoring);
-    assert!(state.current_epoch == current_epoch);
     assert!(state.num_pool_validators == 4);
 
     // 2) Progress stalled and time moved into next epoch
     // Conditions: clock.epoch > state.current_epoch and !state.progress.is_empty()
-    state.current_epoch = current_epoch - 1;
-    assert!(!state.progress.is_empty());
-    assert!(state.current_epoch < clock.epoch);
-    let res = state.compute_score(
-        clock,
-        epoch_schedule,
-        &validators[0],
-        validators[0].index as usize,
-        cluster_history,
-        config,
-        state.num_pool_validators,
-    );
-    assert!(res.is_ok());
-    assert!(state.current_epoch == current_epoch);
+    // REDACTED: The epoch is now updated in the epoch_maintenance method
+
+    // state.current_epoch = current_epoch - 1;
+    // assert!(!state.progress.is_empty());
+    // assert!(state.current_epoch < clock.epoch);
+    // let res = state.compute_score(
+    //     clock,
+    //     epoch_schedule,
+    //     &validators[0],
+    //     validators[0].index as usize,
+    //     cluster_history,
+    //     config,
+    //     state.num_pool_validators,
+    // );
+    // assert!(res.is_ok());
+    // assert!(state.current_epoch == current_epoch);
 
     // 3) Progress started, but took >1000 slots to complete
     // Conditions: start_computing_scores_slot > 1000 slots ago, !progress.is_empty(), and clock.epoch == state.current_epoch
@@ -225,6 +227,8 @@ fn test_compute_scores() {
         state.num_pool_validators,
     );
     assert!(res.is_ok());
+    println!("{:?}", state.start_computing_scores_slot);
+    println!("{:?}", clock.slot);
     assert!(state.start_computing_scores_slot == clock.slot);
 }
 
@@ -427,10 +431,22 @@ fn test_compute_instant_unstake_success() {
         .get(validators[0].index as usize)
         .unwrap());
 
+    // Should skip validator since it's already been computed
+    let res = state.compute_instant_unstake(
+        clock,
+        epoch_schedule,
+        &validators[0],
+        validators[0].index as usize,
+        cluster_history,
+        config,
+    );
+    assert!(res.is_ok());
+
     // Instant unstakeable validator
+    state.progress.reset();
     state.instant_unstake.reset();
     config
-        .blacklist
+        .validator_history_blacklist
         .set(validators[0].index as usize, true)
         .unwrap();
 
@@ -450,6 +466,7 @@ fn test_compute_instant_unstake_success() {
 
     // Instant unstakeable validator with no delegation amount
     state.delegations[validators[0].index as usize] = Delegation::new(0, 1);
+    state.progress.reset();
     state.instant_unstake.reset();
     let res = state.compute_instant_unstake(
         clock,
@@ -571,6 +588,24 @@ fn test_rebalance() {
         _ => panic!("Expected RebalanceType::Decrease"),
     }
 
+    // Test that rebalance will be skipped if validator has already been run
+    let res = state.rebalance(
+        fixtures.current_epoch,
+        1,
+        &validator_list_bigvec,
+        4000 * LAMPORTS_PER_SOL,
+        1000 * LAMPORTS_PER_SOL,
+        0,
+        0,
+        &fixtures.config.parameters,
+    );
+
+    assert!(res.is_ok());
+    match res.unwrap() {
+        RebalanceType::None => {}
+        _ => panic!("Expected RebalanceType::None"),
+    }
+
     // Instant unstake validator, but no delegation, so other delegations are not affected
     // Same scenario as above but out-of-band validator
     state.delegations[0..3].copy_from_slice(&[
@@ -589,6 +624,7 @@ fn test_rebalance() {
     // Validator index 1: 1000 SOL, 0.5 score, 0 delegation, -> Decrease stake, from "instant unstake" category, and set delegation to 0
     // Validator index 2: 1000 SOL, 0 score, 0 delegation -> Decrease stake, from "regular unstake" category
 
+    state.progress.reset();
     let res = state.rebalance(
         fixtures.current_epoch,
         1,
@@ -639,6 +675,8 @@ fn test_rebalance() {
     state.sorted_yield_score_indices[0..3].copy_from_slice(&[0, 1, 2]);
     state.instant_unstake.reset();
     state.instant_unstake.set(0, true).unwrap();
+
+    state.progress.reset();
     let res = state.rebalance(
         fixtures.current_epoch,
         0,
@@ -670,6 +708,7 @@ fn test_rebalance() {
     state.scores[0..3].copy_from_slice(&[1_000_000_000, 1_000_000_000, 1_000_000_000]);
     state.sorted_score_indices[0..3].copy_from_slice(&[0, 1, 2]);
     state.sorted_yield_score_indices[0..3].copy_from_slice(&[0, 1, 2]);
+    state.progress.reset();
     let res = state.rebalance(
         fixtures.current_epoch,
         0,
diff --git a/tests/tests/steward/test_state_transitions.rs b/tests/tests/steward/test_state_transitions.rs
index 75e3eea8..01f4ab7b 100644
--- a/tests/tests/steward/test_state_transitions.rs
+++ b/tests/tests/steward/test_state_transitions.rs
@@ -3,7 +3,9 @@
     These tests cover all possible state transitions when calling the `transition` method on the `StewardState` struct.
 */
 
-use jito_steward::{constants::MAX_VALIDATORS, Delegation, StewardStateEnum};
+use jito_steward::{
+    constants::MAX_VALIDATORS, Delegation, StewardStateEnum, REBALANCE, RESET_TO_IDLE,
+};
 use tests::steward_fixtures::StateMachineFixtures;
 
 #[test]
@@ -214,7 +216,7 @@ pub fn test_idle_noop() {
 
     // Case 2: still after instant_unstake_epoch_progress but after rebalance is completed
     clock.slot = epoch_schedule.get_last_slot_in_epoch(clock.epoch);
-    state.rebalance_completed = true.into();
+    state.set_flag(REBALANCE);
     let res = state.transition(clock, parameters, epoch_schedule);
     assert!(res.is_ok());
     assert!(matches!(state.state_tag, StewardStateEnum::Idle));
@@ -274,15 +276,13 @@ pub fn test_compute_instant_unstake_to_rebalance() {
 pub fn test_compute_instant_unstake_to_idle() {
     let mut fixtures = Box::<StateMachineFixtures>::default();
 
-    let current_epoch = fixtures.clock.epoch;
     let clock = &mut fixtures.clock;
     let epoch_schedule = &fixtures.epoch_schedule;
     let parameters = &fixtures.config.parameters;
     let state = &mut fixtures.state;
 
     state.state_tag = StewardStateEnum::ComputeInstantUnstake;
-    clock.epoch = current_epoch + 1;
-    clock.slot = epoch_schedule.get_last_slot_in_epoch(clock.epoch);
+    state.set_flag(RESET_TO_IDLE);
 
     let res = state.transition(clock, parameters, epoch_schedule);
     assert!(res.is_ok());
@@ -350,8 +350,8 @@ pub fn test_rebalance_to_idle() {
     // Test didn't finish rebalance case
     state.state_tag = StewardStateEnum::Rebalance;
     state.progress.reset();
-    clock.epoch += 1;
-    clock.slot = epoch_schedule.get_last_slot_in_epoch(clock.epoch);
+    state.set_flag(RESET_TO_IDLE);
+
     let res = state.transition(clock, parameters, epoch_schedule);
     assert!(res.is_ok());
     assert!(matches!(state.state_tag, StewardStateEnum::Idle));
diff --git a/tests/tests/steward/test_steward.rs b/tests/tests/steward/test_steward.rs
index 5e7673d1..73c30e81 100644
--- a/tests/tests/steward/test_steward.rs
+++ b/tests/tests/steward/test_steward.rs
@@ -3,12 +3,14 @@ use anchor_lang::{
     solana_program::{instruction::Instruction, pubkey::Pubkey, stake, sysvar},
     InstructionData, ToAccountMetas,
 };
-use jito_steward::{utils::ValidatorList, Config, StewardStateAccount};
+use jito_steward::{
+    instructions::AuthorityType, utils::ValidatorList, Config, StewardStateAccount,
+};
 use solana_program_test::*;
 use solana_sdk::{signature::Keypair, signer::Signer, transaction::Transaction};
 use tests::steward_fixtures::{
-    closed_vote_account, new_vote_account, serialized_steward_state_account,
-    serialized_validator_history_account, system_account, validator_history_default, TestFixture,
+    closed_vote_account, new_vote_account, serialized_validator_history_account, system_account,
+    validator_history_default, TestFixture,
 };
 use validator_history::{ValidatorHistory, ValidatorHistoryEntry};
 
@@ -41,11 +43,11 @@ async fn _auto_add_validator_to_pool(fixture: &TestFixture, vote_account: &Pubke
     let add_validator_to_pool_ix = Instruction {
         program_id: jito_steward::id(),
         accounts: jito_steward::accounts::AutoAddValidator {
+            steward_state: fixture.steward_state,
             validator_history_account,
             config: fixture.steward_config.pubkey(),
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             reserve_stake: fixture.stake_pool_meta.reserve,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
@@ -57,7 +59,6 @@ async fn _auto_add_validator_to_pool(fixture: &TestFixture, vote_account: &Pubke
             stake_config: stake::config::ID,
             stake_program: stake::program::id(),
             system_program: solana_program::system_program::id(),
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::AutoAddValidatorToPool {}.data(),
@@ -77,6 +78,7 @@ async fn _auto_add_validator_to_pool(fixture: &TestFixture, vote_account: &Pubke
     for i in 0..20 {
         validator_history.history.push(ValidatorHistoryEntry {
             epoch: i,
+            activated_stake_lamports: 100_000_000_000_000,
             epoch_credits: 400000,
             vote_account_last_update_slot: 100,
             ..ValidatorHistoryEntry::default()
@@ -107,8 +109,8 @@ async fn test_auto_add_validator_to_pool() {
     let fixture = TestFixture::new().await;
 
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     _auto_add_validator_to_pool(&fixture, &Pubkey::new_unique()).await;
 
@@ -120,8 +122,8 @@ async fn test_auto_remove() {
     let fixture = TestFixture::new().await;
 
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
-    fixture.initialize_steward_state().await;
+    fixture.initialize_steward(None).await;
+    fixture.realloc_steward_state().await;
 
     let vote_account = Pubkey::new_unique();
 
@@ -134,6 +136,7 @@ async fn test_auto_remove() {
         fixture.stake_accounts_for_validator(vote_account).await;
 
     // Add vote account
+
     _auto_add_validator_to_pool(&fixture, &vote_account).await;
 
     let auto_remove_validator_ix = Instruction {
@@ -144,7 +147,6 @@ async fn test_auto_remove() {
             state_account: fixture.steward_state,
             stake_pool_program: spl_stake_pool::id(),
             stake_pool: fixture.stake_pool_meta.stake_pool,
-            staker: fixture.staker,
             reserve_stake: fixture.stake_pool_meta.reserve,
             withdraw_authority,
             validator_list: fixture.stake_pool_meta.validator_list,
@@ -157,7 +159,6 @@ async fn test_auto_remove() {
             stake_config: stake::config::ID,
             stake_program: stake::program::id(),
             system_program: solana_program::system_program::id(),
-            signer: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
         data: jito_steward::instruction::AutoRemoveValidatorFromPool {
@@ -166,19 +167,6 @@ async fn test_auto_remove() {
         .data(),
     };
 
-    let mut steward_state_account: StewardStateAccount =
-        fixture.load_and_deserialize(&fixture.steward_state).await;
-
-    // Fake add vote account to state
-    steward_state_account.state.num_pool_validators = 1;
-    steward_state_account.state.sorted_score_indices[0] = 0;
-    steward_state_account.state.sorted_yield_score_indices[0] = 0;
-
-    fixture.ctx.borrow_mut().set_account(
-        &fixture.steward_state,
-        &serialized_steward_state_account(steward_state_account).into(),
-    );
-
     let tx = Transaction::new_signed_with_payer(
         &[auto_remove_validator_ix],
         Some(&fixture.keypair.pubkey()),
@@ -198,6 +186,43 @@ async fn test_auto_remove() {
 
     fixture.submit_transaction_assert_success(tx).await;
 
+    let steward_state_account: StewardStateAccount =
+        fixture.load_and_deserialize(&fixture.steward_state).await;
+
+    assert!(
+        steward_state_account
+            .state
+            .validators_for_immediate_removal
+            .count()
+            == 1
+    );
+
+    let instant_remove_validator_ix = Instruction {
+        program_id: jito_steward::id(),
+        accounts: jito_steward::accounts::InstantRemoveValidator {
+            config: fixture.steward_config.pubkey(),
+            state_account: fixture.steward_state,
+            validator_list: fixture.stake_pool_meta.validator_list,
+            stake_pool: fixture.stake_pool_meta.stake_pool,
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::InstantRemoveValidator {
+            validator_index_to_remove: 0,
+        }
+        .data(),
+    };
+
+    let tx = Transaction::new_signed_with_payer(
+        &[instant_remove_validator_ix.clone()],
+        Some(&fixture.keypair.pubkey()),
+        &[&fixture.keypair],
+        fixture.ctx.borrow().last_blockhash,
+    );
+
+    fixture
+        .submit_transaction_assert_error(tx, "ValidatorsHaveNotBeenRemoved")
+        .await;
+
     drop(fixture);
 }
 
@@ -206,7 +231,7 @@ async fn test_pause() {
     let fixture = TestFixture::new().await;
     let ctx = &fixture.ctx;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
+    fixture.initialize_steward(None).await;
 
     let ix = Instruction {
         program_id: jito_steward::id(),
@@ -265,7 +290,7 @@ async fn test_blacklist() {
     let fixture = TestFixture::new().await;
     let ctx = &fixture.ctx;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
+    fixture.initialize_steward(None).await;
 
     let ix = Instruction {
         program_id: jito_steward::id(),
@@ -274,7 +299,10 @@ async fn test_blacklist() {
             authority: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
-        data: jito_steward::instruction::AddValidatorToBlacklist { index: 0 }.data(),
+        data: jito_steward::instruction::AddValidatorToBlacklist {
+            validator_history_blacklist: 0,
+        }
+        .data(),
     };
 
     let tx = Transaction::new_signed_with_payer(
@@ -289,7 +317,7 @@ async fn test_blacklist() {
     let config: Config = fixture
         .load_and_deserialize(&fixture.steward_config.pubkey())
         .await;
-    assert!(config.blacklist.get(0).unwrap());
+    assert!(config.validator_history_blacklist.get(0).unwrap());
 
     let ix = Instruction {
         program_id: jito_steward::id(),
@@ -298,7 +326,10 @@ async fn test_blacklist() {
             authority: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
-        data: jito_steward::instruction::RemoveValidatorFromBlacklist { index: 0 }.data(),
+        data: jito_steward::instruction::RemoveValidatorFromBlacklist {
+            validator_history_blacklist: 0,
+        }
+        .data(),
     };
 
     let tx = Transaction::new_signed_with_payer(
@@ -312,7 +343,7 @@ async fn test_blacklist() {
     let config: Config = fixture
         .load_and_deserialize(&fixture.steward_config.pubkey())
         .await;
-    assert!(!config.blacklist.get(0).unwrap());
+    assert!(!config.validator_history_blacklist.get(0).unwrap());
 
     drop(fixture);
 }
@@ -322,7 +353,7 @@ async fn test_set_new_authority() {
     let fixture = TestFixture::new().await;
     let ctx = &fixture.ctx;
     fixture.initialize_stake_pool().await;
-    fixture.initialize_config(None).await;
+    fixture.initialize_steward(None).await;
 
     // Regular test
     let new_authority = Keypair::new();
@@ -336,10 +367,13 @@ async fn test_set_new_authority() {
         accounts: jito_steward::accounts::SetNewAuthority {
             config: fixture.steward_config.pubkey(),
             new_authority: new_authority.pubkey(),
-            authority: fixture.keypair.pubkey(),
+            admin: fixture.keypair.pubkey(),
         }
         .to_account_metas(None),
-        data: jito_steward::instruction::SetNewAuthority {}.data(),
+        data: jito_steward::instruction::SetNewAuthority {
+            authority_type: AuthorityType::SetAdmin,
+        }
+        .data(),
     };
     let tx = Transaction::new_signed_with_payer(
         &[ix],
@@ -353,7 +387,58 @@ async fn test_set_new_authority() {
     let config: Config = fixture
         .load_and_deserialize(&fixture.steward_config.pubkey())
         .await;
-    assert!(config.authority == new_authority.pubkey());
+    assert!(config.admin == new_authority.pubkey());
+
+    let ix = Instruction {
+        program_id: jito_steward::id(),
+        accounts: jito_steward::accounts::SetNewAuthority {
+            config: fixture.steward_config.pubkey(),
+            new_authority: new_authority.pubkey(),
+            admin: new_authority.pubkey(),
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::SetNewAuthority {
+            authority_type: AuthorityType::SetBlacklistAuthority,
+        }
+        .data(),
+    };
+    let tx = Transaction::new_signed_with_payer(
+        &[ix],
+        Some(&new_authority.pubkey()),
+        &[&new_authority],
+        ctx.borrow().last_blockhash,
+    );
+
+    fixture.submit_transaction_assert_success(tx).await;
+
+    let ix = Instruction {
+        program_id: jito_steward::id(),
+        accounts: jito_steward::accounts::SetNewAuthority {
+            config: fixture.steward_config.pubkey(),
+            new_authority: new_authority.pubkey(),
+            admin: new_authority.pubkey(),
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::SetNewAuthority {
+            authority_type: AuthorityType::SetParametersAuthority,
+        }
+        .data(),
+    };
+    let tx = Transaction::new_signed_with_payer(
+        &[ix],
+        Some(&new_authority.pubkey()),
+        &[&new_authority],
+        ctx.borrow().last_blockhash,
+    );
+
+    fixture.submit_transaction_assert_success(tx).await;
+
+    let config: Config = fixture
+        .load_and_deserialize(&fixture.steward_config.pubkey())
+        .await;
+    assert!(config.admin == new_authority.pubkey());
+    assert!(config.blacklist_authority == new_authority.pubkey());
+    assert!(config.parameters_authority == new_authority.pubkey());
 
     // Try to transfer back with original authority
     let ix = Instruction {
@@ -361,21 +446,29 @@ async fn test_set_new_authority() {
         accounts: jito_steward::accounts::SetNewAuthority {
             config: fixture.steward_config.pubkey(),
             new_authority: fixture.keypair.pubkey(),
-            authority: fixture.keypair.pubkey(),
+            admin: new_authority.pubkey(),
         }
         .to_account_metas(None),
-        data: jito_steward::instruction::SetNewAuthority {}.data(),
+        data: jito_steward::instruction::SetNewAuthority {
+            authority_type: AuthorityType::SetAdmin,
+        }
+        .data(),
     };
     let tx = Transaction::new_signed_with_payer(
         &[ix],
-        Some(&fixture.keypair.pubkey()),
-        &[&fixture.keypair],
+        Some(&new_authority.pubkey()),
+        &[&new_authority],
         ctx.borrow().last_blockhash,
     );
 
-    fixture
-        .submit_transaction_assert_error(tx, "Unauthorized")
+    fixture.submit_transaction_assert_success(tx).await;
+
+    let config: Config = fixture
+        .load_and_deserialize(&fixture.steward_config.pubkey())
         .await;
+    assert!(config.admin == fixture.keypair.pubkey());
+    assert!(config.blacklist_authority == new_authority.pubkey());
+    assert!(config.parameters_authority == new_authority.pubkey());
 
     drop(fixture);
 }
diff --git a/utils/steward-cli/Cargo.toml b/utils/steward-cli/Cargo.toml
new file mode 100644
index 00000000..8134dbb6
--- /dev/null
+++ b/utils/steward-cli/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "steward-cli"
+version = "0.1.0"
+edition = "2021"
+description = "CLI to manage the steward program"
+
+[dependencies]
+anchor-lang = "0.30.0"
+anyhow = "1.0.86"
+clap = { version = "4.3.0", features = ["derive", "env"] }
+dotenv = "0.15.0"
+futures = "0.3.21"
+futures-util = "0.3.21"
+jito-steward = { features = ["no-entrypoint"], path = "../../programs/steward" }
+keeper-core = { path = "../../keepers/keeper-core" }
+log = "0.4.18"
+solana-account-decoder = "1.18"
+solana-clap-utils = "1.18"
+solana-client = "1.18"
+solana-metrics = "1.18"
+solana-program = "1.18"
+solana-sdk = "1.18"
+spl-stake-pool = { features = ["no-entrypoint"], version = "1.0.0" }
+thiserror = "1.0.37"
+tokio = { version = "1.36.0", features = ["full"] }
+validator-history = { features = ["no-entrypoint"], path = "../../programs/validator-history" }
diff --git a/utils/steward-cli/initial_notes.md b/utils/steward-cli/initial_notes.md
new file mode 100644
index 00000000..fe3ab76a
--- /dev/null
+++ b/utils/steward-cli/initial_notes.md
@@ -0,0 +1,160 @@
+
+# Accounts
+
+**Authority** 
+`aaaDerwdMyzNkoX1aSoTi3UtFe2W45vh5wCgQNhsjF8`
+
+**Steward Config**   
+`6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5`
+
+**Stake Pool**
+`3DuPtyTAKrxKfHkSPZ5fqCayMcGru1BarAKKTfGDeo2j`
+
+**Staker**
+`4m64H5TbwAGtZVnxaGAVoTSwjZGV8BCLKRPr8agKQv4Z`
+
+**State**
+`6SJrBTYSSu3jWmsPWWhMMHvrPxqKWXtLe9tRfYpU8EZa`
+
+# Initial Commands
+
+## Create Config
+```bash
+cargo run init-config \
+  --authority-keypair-path ../../credentials/stakenet_test.json \
+  --steward-config-keypair-path ../../credentials/steward_config.json \
+  --stake-pool 3DuPtyTAKrxKfHkSPZ5fqCayMcGru1BarAKKTfGDeo2j \
+  --mev-commission-range 10 \
+  --epoch-credits-range 30 \
+  --commission-range 30 \
+  --mev-commission-bps-threshold 1000 \
+  --commission-threshold 5 \
+  --historical-commission-threshold 50 \
+  --scoring-delinquency-threshold-ratio 0.85 \
+  --instant-unstake-delinquency-threshold-ratio 0.70 \
+  --num-delegation-validators 200 \
+  --scoring-unstake-cap-bps 750 \
+  --instant-unstake-cap-bps 1000 \
+  --stake-deposit-unstake-cap-bps 1000 \
+  --compute-score-slot-range 50000 \
+  --instant-unstake-epoch-progress 0.50 \
+  --instant-unstake-inputs-epoch-progress 0.50 \
+  --num-epochs-between-scoring 3 \
+  --minimum-stake-lamports 100000000000 \
+  --minimum-voting-epochs 5
+```
+
+## Update Config
+```bash
+cargo run update-config \
+  --authority-keypair-path ../../credentials/stakenet_test.json \
+  --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 \
+  --instant-unstake-inputs-epoch-progress 0.10 \
+  --instant-unstake-epoch-progress 0.10
+```
+
+## Create State
+```bash
+cargo run init-state --authority-keypair-path ../../credentials/stakenet_test.json --stake-pool 3DuPtyTAKrxKfHkSPZ5fqCayMcGru1BarAKKTfGDeo2j --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5
+```
+
+## View Config
+```bash
+cargo run view-config --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5
+```
+
+## View State
+```bash
+cargo run view-state --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5
+```
+
+## View State Per Validator
+```bash
+cargo run view-state-per-validator --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5
+```
+
+## View Next Index To Remove
+```bash
+cargo run view-next-index-to-remove --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5
+```
+
+## Auto Remove Validator
+```bash
+cargo run auto-remove-validator-from-pool --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 --payer-keypair-path ../../credentials/stakenet_test.json --validator-index-to-remove 1397
+```
+
+## Auto Add Validator
+```bash
+cargo run auto-add-validator-from-pool --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 --payer-keypair-path ../../credentials/stakenet_test.json --vote-account 4m64H5TbwAGtZVnxaGAVoTSwjZGV8BCLKRPr8agKQv4Z 
+```
+
+## Remove Bad Validators
+```bash
+cargo run remove-bad-validators --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 --payer-keypair-path ../../credentials/stakenet_test.json
+```
+
+## Crank Epoch Maintenance
+```bash
+cargo run crank-epoch-maintenance --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 --payer-keypair-path ../../credentials/stakenet_test.json
+```
+
+## Crank Compute Score
+```bash
+cargo run crank-compute-score --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 --payer-keypair-path ../../credentials/stakenet_test.json
+```
+
+## Crank Compute Delegations
+```bash
+cargo run crank-compute-delegations --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 --payer-keypair-path ../../credentials/stakenet_test.json
+```
+
+## Crank Idle
+```bash
+cargo run crank-idle --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 --payer-keypair-path ../../credentials/stakenet_test.json
+```
+
+## Crank Compute Instant Unstake
+```bash
+cargo run crank-compute-instant-unstake --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 --payer-keypair-path ../../credentials/stakenet_test.json
+```
+
+## Crank Rebalance
+```bash
+cargo run crank-rebalance --steward-config 6auT7Q91SSgAoYLAnu449DK1MK9skDmtiLmtkCECP1b5 --payer-keypair-path ../../credentials/stakenet_test.json
+```
+
+# Deploy and Upgrade
+
+- upgrade solana cli to 1.18.16
+- make sure your configured keypair is `aaaDerwdMyzNkoX1aSoTi3UtFe2W45vh5wCgQNhsjF8`
+- create a new keypair: `solana-keygen new -o credentials/temp-buffer.json`
+- use anchor `0.30.0`: `avm install 0.30.0 && avm use 0.30.0`
+- build .so file: `anchor build --no-idl`
+- Write to buffer: `solana program write-buffer --use-rpc --buffer credentials/temp-buffer.json --url $(solana config get | grep "RPC URL" | awk '{print $3}') --with-compute-unit-price 10000 --max-sign-attempts 10000 target/deploy/jito_steward.so --keypair credentials/stakenet_test.json`
+- Upgrade: `solana program upgrade $(solana address --keypair credentials/temp-buffer.json) sssh4zkKhX8jXTNQz1xDHyGpygzgu2UhcRcUvZihBjP --keypair credentials/stakenet_test.json --url $(solana config get | grep "RPC URL" | awk '{print $3}')`
+- Close Buffers: `solana program close --buffers --keypair credentials/stakenet_test.json`
+- Upgrade Program Size: `solana program extend sssh4zkKhX8jXTNQz1xDHyGpygzgu2UhcRcUvZihBjP 1000000 --keypair credentials/stakenet_test.json --url $(solana config get | grep "RPC URL" | awk '{print $3}')`
+
+# Initial Parameters
+
+```env
+# Note - Do not use this .env when updating the parameters - this will update them all
+MEV_COMMISSION_RANGE=10
+EPOCH_CREDITS_RANGE=30
+COMMISSION_RANGE=30
+MEV_COMMISSION_BPS_THRESHOLD=1000
+COMMISSION_THRESHOLD=5
+HISTORICAL_COMMISSION_THRESHOLD=50
+SCORING_DELINQUENCY_THRESHOLD_RATIO=0.85
+INSTANT_UNSTAKE_DELINQUENCY_THRESHOLD_RATIO=0.70
+NUM_DELEGATION_VALIDATORS=200
+SCORING_UNSTAKE_CAP_BPS=750
+INSTANT_UNSTAKE_CAP_BPS=1000
+STAKE_DEPOSIT_UNSTAKE_CAP_BPS=1000
+COMPUTE_SCORE_SLOT_RANGE=1000
+INSTANT_UNSTAKE_EPOCH_PROGRESS=0.50
+INSTANT_UNSTAKE_INPUTS_EPOCH_PROGRESS=0.50
+NUM_EPOCHS_BETWEEN_SCORING=3
+MINIMUM_STAKE_LAMPORTS=100000000000
+MINIMUM_VOTING_EPOCHS=5
+```
\ No newline at end of file
diff --git a/utils/steward-cli/src/commands/actions/auto_add_validator_from_pool.rs b/utils/steward-cli/src/commands/actions/auto_add_validator_from_pool.rs
new file mode 100644
index 00000000..9bcea96d
--- /dev/null
+++ b/utils/steward-cli/src/commands/actions/auto_add_validator_from_pool.rs
@@ -0,0 +1,107 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+use spl_stake_pool::find_stake_program_address;
+use validator_history::id as validator_history_id;
+
+use solana_sdk::{
+    pubkey::Pubkey, signature::read_keypair_file, signer::Signer, stake, system_program,
+    transaction::Transaction,
+};
+
+use crate::{
+    commands::command_args::AutoAddValidatorFromPool,
+    utils::{
+        accounts::{get_all_steward_accounts, get_validator_history_address},
+        transactions::configure_instruction,
+    },
+};
+
+pub async fn command_auto_add_validator_from_pool(
+    args: AutoAddValidatorFromPool,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    // Creates config account
+    let payer = Arc::new(
+        read_keypair_file(args.permissionless_parameters.payer_keypair_path)
+            .expect("Failed reading keypair file ( Payer )"),
+    );
+
+    let validator_history_program_id = validator_history_id();
+    let steward_config = args.permissionless_parameters.steward_config;
+
+    let steward_accounts = get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    let vote_account = args.vote_account;
+    let history_account =
+        get_validator_history_address(&vote_account, &validator_history_program_id);
+
+    let (stake_address, _) = find_stake_program_address(
+        &spl_stake_pool::id(),
+        &vote_account,
+        &steward_accounts.stake_pool_address,
+        None,
+    );
+
+    let ix = Instruction {
+        program_id,
+        accounts: jito_steward::accounts::AutoAddValidator {
+            validator_history_account: history_account,
+            steward_state: steward_accounts.state_address,
+            config: args.permissionless_parameters.steward_config,
+            stake_pool_program: spl_stake_pool::id(),
+            stake_pool: steward_accounts.stake_pool_address,
+            reserve_stake: steward_accounts.stake_pool_account.reserve_stake,
+            withdraw_authority: steward_accounts.stake_pool_withdraw_authority,
+            validator_list: steward_accounts.validator_list_address,
+            stake_account: stake_address,
+            vote_account,
+            rent: solana_sdk::sysvar::rent::id(),
+            clock: solana_sdk::sysvar::clock::id(),
+            stake_history: solana_sdk::sysvar::stake_history::id(),
+            stake_config: stake::config::ID,
+            system_program: system_program::id(),
+            stake_program: stake::program::id(),
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::AutoAddValidatorToPool {}.data(),
+    };
+
+    let blockhash = client
+        .get_latest_blockhash()
+        .await
+        .expect("Failed to get recent blockhash");
+
+    let configured_ix = configure_instruction(
+        &[ix],
+        args.permissionless_parameters
+            .transaction_parameters
+            .priority_fee,
+        args.permissionless_parameters
+            .transaction_parameters
+            .compute_limit,
+        args.permissionless_parameters
+            .transaction_parameters
+            .heap_size,
+    );
+
+    let transaction = Transaction::new_signed_with_payer(
+        &configured_ix,
+        Some(&payer.pubkey()),
+        &[&payer],
+        blockhash,
+    );
+
+    let signature = client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .expect("Failed to send transaction");
+    println!("Signature: {}", signature);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/actions/auto_remove_validator_from_pool.rs b/utils/steward-cli/src/commands/actions/auto_remove_validator_from_pool.rs
new file mode 100644
index 00000000..d6cd47be
--- /dev/null
+++ b/utils/steward-cli/src/commands/actions/auto_remove_validator_from_pool.rs
@@ -0,0 +1,117 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+use spl_stake_pool::{find_stake_program_address, find_transient_stake_program_address};
+use validator_history::id as validator_history_id;
+
+use solana_sdk::{
+    pubkey::Pubkey, signature::read_keypair_file, signer::Signer, stake, system_program,
+    transaction::Transaction,
+};
+
+use crate::{
+    commands::command_args::AutoRemoveValidatorFromPool,
+    utils::{
+        accounts::{get_all_steward_accounts, get_validator_history_address},
+        transactions::configure_instruction,
+    },
+};
+
+pub async fn command_auto_remove_validator_from_pool(
+    args: AutoRemoveValidatorFromPool,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    let validator_index = args.validator_index_to_remove;
+    let args = args.permissionless_parameters;
+
+    // Creates config account
+    let payer = Arc::new(
+        read_keypair_file(args.payer_keypair_path).expect("Failed reading keypair file ( Payer )"),
+    );
+
+    let validator_history_program_id = validator_history_id();
+    let steward_config = args.steward_config;
+
+    let steward_accounts = get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    let vote_account =
+        steward_accounts.validator_list_account.validators[validator_index].vote_account_address;
+    let history_account =
+        get_validator_history_address(&vote_account, &validator_history_program_id);
+
+    let (stake_address, _) = find_stake_program_address(
+        &spl_stake_pool::id(),
+        &vote_account,
+        &steward_accounts.stake_pool_address,
+        None,
+    );
+
+    let (transient_stake_address, _) = find_transient_stake_program_address(
+        &spl_stake_pool::id(),
+        &vote_account,
+        &steward_accounts.stake_pool_address,
+        steward_accounts.validator_list_account.validators[validator_index]
+            .transient_seed_suffix
+            .into(),
+    );
+
+    let ix = Instruction {
+        program_id,
+        accounts: jito_steward::accounts::AutoRemoveValidator {
+            validator_history_account: history_account,
+            config: args.steward_config,
+            state_account: steward_accounts.state_address,
+            stake_pool_program: spl_stake_pool::id(),
+            stake_pool: steward_accounts.stake_pool_address,
+            reserve_stake: steward_accounts.stake_pool_account.reserve_stake,
+            withdraw_authority: steward_accounts.stake_pool_withdraw_authority,
+            validator_list: steward_accounts.validator_list_address,
+            stake_account: stake_address,
+            transient_stake_account: transient_stake_address,
+            vote_account,
+            rent: solana_sdk::sysvar::rent::id(),
+            clock: solana_sdk::sysvar::clock::id(),
+            stake_history: solana_sdk::sysvar::stake_history::id(),
+            stake_config: stake::config::ID,
+            system_program: system_program::id(),
+            stake_program: stake::program::id(),
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::AutoRemoveValidatorFromPool {
+            validator_list_index: validator_index as u64,
+        }
+        .data(),
+    };
+
+    let blockhash = client
+        .get_latest_blockhash()
+        .await
+        .expect("Failed to get recent blockhash");
+
+    let configured_ix = configure_instruction(
+        &[ix],
+        args.transaction_parameters.priority_fee,
+        args.transaction_parameters.compute_limit,
+        args.transaction_parameters.heap_size,
+    );
+
+    let transaction = Transaction::new_signed_with_payer(
+        &configured_ix,
+        Some(&payer.pubkey()),
+        &[&payer],
+        blockhash,
+    );
+
+    let signature = client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .expect("Failed to send transaction");
+    println!("Signature: {}", signature);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/actions/mod.rs b/utils/steward-cli/src/commands/actions/mod.rs
new file mode 100644
index 00000000..26b60d27
--- /dev/null
+++ b/utils/steward-cli/src/commands/actions/mod.rs
@@ -0,0 +1,5 @@
+pub mod auto_add_validator_from_pool;
+pub mod auto_remove_validator_from_pool;
+pub mod remove_bad_validators;
+pub mod reset_state;
+pub mod update_config;
diff --git a/utils/steward-cli/src/commands/actions/remove_bad_validators.rs b/utils/steward-cli/src/commands/actions/remove_bad_validators.rs
new file mode 100644
index 00000000..e7740d6b
--- /dev/null
+++ b/utils/steward-cli/src/commands/actions/remove_bad_validators.rs
@@ -0,0 +1,157 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+
+use keeper_core::{get_multiple_accounts_batched, submit_transactions};
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+use spl_stake_pool::{find_stake_program_address, find_transient_stake_program_address};
+use validator_history::id as validator_history_id;
+
+use solana_sdk::{
+    pubkey::Pubkey, signature::read_keypair_file, signer::Signer, stake, system_program, sysvar,
+};
+
+use crate::{
+    commands::command_args::RemoveBadValidators,
+    utils::{
+        accounts::{get_all_steward_accounts, get_validator_history_address},
+        transactions::package_instructions,
+    },
+};
+
+pub async fn command_remove_bad_validators(
+    args: RemoveBadValidators,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    // Creates config account
+    let payer = read_keypair_file(args.permissioned_parameters.authority_keypair_path)
+        .expect("Failed reading keypair file ( Payer )");
+    let arc_payer = Arc::new(payer);
+
+    let validator_history_program_id = validator_history_id();
+    let steward_config = args.permissioned_parameters.steward_config;
+
+    let steward_accounts = get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    let validators_to_run = (0..steward_accounts.validator_list_account.validators.len())
+        .filter_map(|validator_index| {
+            let has_been_scored = steward_accounts
+                .state_account
+                .state
+                .progress
+                .get(validator_index)
+                .expect("Index is not in progress bitmask");
+            if has_been_scored {
+                None
+            } else {
+                let vote_account = steward_accounts.validator_list_account.validators
+                    [validator_index]
+                    .vote_account_address;
+                let history_account =
+                    get_validator_history_address(&vote_account, &validator_history_program_id);
+
+                Some((validator_index, vote_account, history_account))
+            }
+        })
+        .collect::<Vec<(usize, Pubkey, Pubkey)>>();
+
+    let history_accounts = validators_to_run
+        .iter()
+        .map(|(_, _, history_account)| *history_account)
+        .collect::<Vec<Pubkey>>();
+
+    let validator_history_accounts =
+        get_multiple_accounts_batched(&history_accounts, client).await?;
+
+    let bad_history_accounts = validator_history_accounts
+        .iter()
+        .zip(validators_to_run)
+        .filter_map(
+            |(account, (index, vote_account, history_account))| match account {
+                Some(_) => None,
+                None => Some((index, vote_account, history_account)),
+            },
+        )
+        .collect::<Vec<(usize, Pubkey, Pubkey)>>();
+
+    println!("Bad history accounts: {:?}", bad_history_accounts);
+
+    let ixs_to_run = bad_history_accounts
+        .iter()
+        .map(|(validator_index, vote_account, history_account)| {
+            println!(
+                "index: {}, vote_account: {}, history_account: {}\n",
+                validator_index, vote_account, history_account
+            );
+
+            let (stake_address, _) = find_stake_program_address(
+                &spl_stake_pool::id(),
+                vote_account,
+                &steward_accounts.stake_pool_address,
+                None,
+            );
+
+            let (transient_stake_address, _) = find_transient_stake_program_address(
+                &spl_stake_pool::id(),
+                vote_account,
+                &steward_accounts.stake_pool_address,
+                steward_accounts.validator_list_account.validators[*validator_index]
+                    .transient_seed_suffix
+                    .into(),
+            );
+
+            Instruction {
+                program_id,
+                accounts: jito_steward::accounts::RemoveValidatorFromPool {
+                    admin: arc_payer.pubkey(),
+                    config: steward_config,
+                    state_account: steward_accounts.state_address,
+                    stake_pool_program: spl_stake_pool::id(),
+                    stake_pool: steward_accounts.stake_pool_address,
+                    withdraw_authority: steward_accounts.stake_pool_withdraw_authority,
+                    validator_list: steward_accounts.validator_list_address,
+                    stake_account: stake_address,
+                    transient_stake_account: transient_stake_address,
+                    clock: sysvar::clock::id(),
+                    system_program: system_program::id(),
+                    stake_program: stake::program::id(),
+                }
+                .to_account_metas(None),
+                data: jito_steward::instruction::RemoveValidatorFromPool {
+                    validator_list_index: *validator_index as u64,
+                }
+                .data(),
+            }
+        })
+        .collect::<Vec<Instruction>>();
+
+    let txs_to_run = package_instructions(
+        &ixs_to_run,
+        args.permissioned_parameters
+            .transaction_parameters
+            .chunk_size
+            .unwrap_or(1),
+        args.permissioned_parameters
+            .transaction_parameters
+            .priority_fee,
+        args.permissioned_parameters
+            .transaction_parameters
+            .compute_limit
+            .or(Some(1_400_000)),
+        args.permissioned_parameters
+            .transaction_parameters
+            .heap_size
+            .or(Some(256 * 1024)),
+    );
+
+    println!("Submitting {} instructions", ixs_to_run.len());
+
+    let submit_stats = submit_transactions(client, txs_to_run, &arc_payer).await?;
+
+    println!("Submit stats: {:?}", submit_stats);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/actions/reset_state.rs b/utils/steward-cli/src/commands/actions/reset_state.rs
new file mode 100644
index 00000000..501f2b3a
--- /dev/null
+++ b/utils/steward-cli/src/commands/actions/reset_state.rs
@@ -0,0 +1,75 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+
+use solana_sdk::{
+    pubkey::Pubkey, signature::read_keypair_file, signer::Signer, transaction::Transaction,
+};
+
+use crate::{
+    commands::command_args::ResetState,
+    utils::{accounts::get_all_steward_accounts, transactions::configure_instruction},
+};
+
+pub async fn command_reset_state(
+    args: ResetState,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    // Creates config account
+    let authority = read_keypair_file(args.permissioned_parameters.authority_keypair_path)
+        .expect("Failed reading keypair file ( Authority )");
+
+    let all_steward_accounts = get_all_steward_accounts(
+        client,
+        &program_id,
+        &args.permissioned_parameters.steward_config,
+    )
+    .await?;
+
+    let ix = Instruction {
+        program_id,
+        accounts: jito_steward::accounts::ResetStewardState {
+            state_account: all_steward_accounts.state_address,
+            config: args.permissioned_parameters.steward_config,
+            stake_pool: all_steward_accounts.stake_pool_address,
+            validator_list: all_steward_accounts.validator_list_address,
+            authority: authority.pubkey(),
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::ResetStewardState {}.data(),
+    };
+
+    let blockhash = client.get_latest_blockhash().await?;
+
+    let configured_ix = configure_instruction(
+        &[ix],
+        args.permissioned_parameters
+            .transaction_parameters
+            .priority_fee,
+        args.permissioned_parameters
+            .transaction_parameters
+            .compute_limit,
+        args.permissioned_parameters
+            .transaction_parameters
+            .heap_size,
+    );
+
+    let transaction = Transaction::new_signed_with_payer(
+        &configured_ix,
+        Some(&authority.pubkey()),
+        &[&authority],
+        blockhash,
+    );
+
+    let signature = client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await?;
+
+    println!("Signature: {}", signature);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/actions/update_config.rs b/utils/steward-cli/src/commands/actions/update_config.rs
new file mode 100644
index 00000000..61bc74c0
--- /dev/null
+++ b/utils/steward-cli/src/commands/actions/update_config.rs
@@ -0,0 +1,73 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+use jito_steward::UpdateParametersArgs;
+
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+use solana_sdk::{
+    pubkey::Pubkey, signature::read_keypair_file, signer::Signer, transaction::Transaction,
+};
+
+use crate::{commands::command_args::UpdateConfig, utils::transactions::configure_instruction};
+
+pub async fn command_update_config(
+    args: UpdateConfig,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    // Creates config account
+    let authority = read_keypair_file(args.permissioned_parameters.authority_keypair_path)
+        .expect("Failed reading keypair file ( Authority )");
+
+    let steward_config = args.permissioned_parameters.steward_config;
+
+    let update_parameters_args: UpdateParametersArgs = args.config_parameters.into();
+
+    let ix = Instruction {
+        program_id,
+        accounts: jito_steward::accounts::UpdateParameters {
+            config: steward_config,
+            authority: authority.pubkey(),
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::UpdateParameters {
+            update_parameters_args,
+        }
+        .data(),
+    };
+
+    let blockhash = client
+        .get_latest_blockhash()
+        .await
+        .expect("Failed to get recent blockhash");
+
+    let configured_ix = configure_instruction(
+        &[ix],
+        args.permissioned_parameters
+            .transaction_parameters
+            .priority_fee,
+        args.permissioned_parameters
+            .transaction_parameters
+            .compute_limit,
+        args.permissioned_parameters
+            .transaction_parameters
+            .heap_size,
+    );
+
+    let transaction = Transaction::new_signed_with_payer(
+        &configured_ix,
+        Some(&authority.pubkey()),
+        &[&authority],
+        blockhash,
+    );
+
+    let signature = client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .expect("Failed to send transaction");
+    println!("Signature: {}", signature);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/command_args.rs b/utils/steward-cli/src/commands/command_args.rs
new file mode 100644
index 00000000..35786910
--- /dev/null
+++ b/utils/steward-cli/src/commands/command_args.rs
@@ -0,0 +1,367 @@
+use clap::{arg, command, Parser, Subcommand};
+use jito_steward::UpdateParametersArgs;
+use solana_sdk::pubkey::Pubkey;
+use std::path::PathBuf;
+
+#[derive(Parser)]
+#[command(about = "CLI for the steward program")]
+pub struct Args {
+    /// RPC URL for the cluster
+    #[arg(
+        short,
+        long,
+        env,
+        default_value = "https://api.mainnet-beta.solana.com"
+    )]
+    pub json_rpc_url: String,
+
+    /// Steward program ID
+    #[arg(
+        long,
+        env,
+        default_value_t = jito_steward::id()
+    )]
+    pub program_id: Pubkey,
+
+    #[command(subcommand)]
+    pub commands: Commands,
+}
+
+// ---------- Meta Parameters ------------
+#[derive(Parser)]
+pub struct ConfigParameters {
+    /// Number of recent epochs used to evaluate MEV commissions and running Jito for scoring
+    #[arg(long, env)]
+    pub mev_commission_range: Option<u16>,
+
+    /// Number of recent epochs used to evaluate yield
+    #[arg(long, env)]
+    pub epoch_credits_range: Option<u16>,
+
+    /// Number of recent epochs used to evaluate commissions for scoring
+    #[arg(long, env)]
+    pub commission_range: Option<u16>,
+
+    /// Minimum ratio of slots voted on for each epoch for a validator to be eligible for stake. Used as proxy for validator reliability/restart timeliness. Ratio is number of epoch_credits / blocks_produced
+    #[arg(long, env)]
+    pub scoring_delinquency_threshold_ratio: Option<f64>,
+
+    /// Same as scoring_delinquency_threshold_ratio but evaluated every epoch
+    #[arg(long, env)]
+    pub instant_unstake_delinquency_threshold_ratio: Option<f64>,
+
+    /// Maximum allowable MEV commission in mev_commission_range (stored in basis points)
+    #[arg(long, env)]
+    pub mev_commission_bps_threshold: Option<u16>,
+
+    /// Maximum allowable validator commission in commission_range (stored in percent)
+    #[arg(long, env)]
+    pub commission_threshold: Option<u8>,
+
+    /// Maximum allowable validator commission in all history (stored in percent)
+    #[arg(long, env)]
+    pub historical_commission_threshold: Option<u8>,
+
+    /// Number of validators who are eligible for stake (validator set size)
+    #[arg(long, env)]
+    pub num_delegation_validators: Option<u32>,
+
+    /// Percent of total pool lamports that can be unstaked due to new delegation set (in basis points)
+    #[arg(long, env)]
+    pub scoring_unstake_cap_bps: Option<u32>,
+
+    /// Percent of total pool lamports that can be unstaked due to instant unstaking (in basis points)
+    #[arg(long, env)]
+    pub instant_unstake_cap_bps: Option<u32>,
+
+    /// Percent of total pool lamports that can be unstaked due to stake deposits above target lamports (in basis points)
+    #[arg(long, env)]
+    pub stake_deposit_unstake_cap_bps: Option<u32>,
+
+    /// Scoring window such that the validators are all scored within a similar timeframe (in slots)
+    #[arg(long, env)]
+    pub compute_score_slot_range: Option<u64>,
+
+    /// Point in epoch progress before instant unstake can be computed
+    #[arg(long, env)]
+    pub instant_unstake_epoch_progress: Option<f64>,
+
+    /// Inputs to ā€œCompute Instant Unstakeā€ need to be updated past this point in epoch progress
+    #[arg(long, env)]
+    pub instant_unstake_inputs_epoch_progress: Option<f64>,
+
+    /// Cycle length - Number of epochs to run the Monitor->Rebalance loop
+    #[arg(long, env)]
+    pub num_epochs_between_scoring: Option<u64>,
+
+    /// Minimum number of stake lamports for a validator to be considered for the pool
+    #[arg(long, env)]
+    pub minimum_stake_lamports: Option<u64>,
+
+    /// Minimum number of consecutive epochs a validator has to vote before it can be considered for the pool
+    #[arg(long, env)]
+    pub minimum_voting_epochs: Option<u64>,
+}
+
+impl From<ConfigParameters> for UpdateParametersArgs {
+    fn from(config: ConfigParameters) -> Self {
+        UpdateParametersArgs {
+            mev_commission_range: config.mev_commission_range,
+            epoch_credits_range: config.epoch_credits_range,
+            commission_range: config.commission_range,
+            scoring_delinquency_threshold_ratio: config.scoring_delinquency_threshold_ratio,
+            instant_unstake_delinquency_threshold_ratio: config
+                .instant_unstake_delinquency_threshold_ratio,
+            mev_commission_bps_threshold: config.mev_commission_bps_threshold,
+            commission_threshold: config.commission_threshold,
+            historical_commission_threshold: config.historical_commission_threshold,
+            num_delegation_validators: config.num_delegation_validators,
+            scoring_unstake_cap_bps: config.scoring_unstake_cap_bps,
+            instant_unstake_cap_bps: config.instant_unstake_cap_bps,
+            stake_deposit_unstake_cap_bps: config.stake_deposit_unstake_cap_bps,
+            compute_score_slot_range: config.compute_score_slot_range,
+            instant_unstake_epoch_progress: config.instant_unstake_epoch_progress,
+            instant_unstake_inputs_epoch_progress: config.instant_unstake_inputs_epoch_progress,
+            num_epochs_between_scoring: config.num_epochs_between_scoring,
+            minimum_stake_lamports: config.minimum_stake_lamports,
+            minimum_voting_epochs: config.minimum_voting_epochs,
+        }
+    }
+}
+
+#[derive(Parser)]
+pub struct TransactionParameters {
+    /// priority fee in microlamports
+    #[arg(long, env)]
+    pub priority_fee: Option<u64>,
+
+    /// CUs per transaction
+    #[arg(long, env)]
+    pub compute_limit: Option<u32>,
+
+    /// Heap size for heap frame
+    #[arg(long, env)]
+    pub heap_size: Option<u32>,
+
+    /// Amount of instructions to process in a single transaction
+    #[arg(long, env)]
+    pub chunk_size: Option<usize>,
+}
+
+#[derive(Parser)]
+pub struct PermissionlessParameters {
+    /// Path to keypair used to pay for the transaction
+    #[arg(short, long, env, default_value = "~/.config/solana/id.json")]
+    pub payer_keypair_path: PathBuf,
+
+    /// Steward config account
+    #[arg(long, env)]
+    pub steward_config: Pubkey,
+
+    #[command(flatten)]
+    pub transaction_parameters: TransactionParameters,
+}
+
+#[derive(Parser)]
+pub struct PermissionedParameters {
+    /// Authority keypair path, also used as payer
+    #[arg(short, long, env, default_value = "~/.config/solana/id.json")]
+    pub authority_keypair_path: PathBuf,
+
+    // Steward config account
+    #[arg(long, env)]
+    pub steward_config: Pubkey,
+
+    #[command(flatten)]
+    pub transaction_parameters: TransactionParameters,
+}
+
+#[derive(Parser)]
+pub struct ViewParameters {
+    /// Steward account
+    #[arg(long, env)]
+    pub steward_config: Pubkey,
+}
+
+// ---------- COMMANDS ------------
+#[derive(Subcommand)]
+pub enum Commands {
+    // Views
+    ViewState(ViewState),
+    ViewConfig(ViewConfig),
+    ViewNextIndexToRemove(ViewNextIndexToRemove),
+
+    // Actions
+    InitConfig(InitConfig),
+    UpdateConfig(UpdateConfig),
+
+    InitState(InitState),
+    ResetState(ResetState),
+
+    RemoveBadValidators(RemoveBadValidators),
+    AutoRemoveValidatorFromPool(AutoRemoveValidatorFromPool),
+    AutoAddValidatorFromPool(AutoAddValidatorFromPool),
+
+    // Cranks
+    CrankEpochMaintenance(CrankEpochMaintenance),
+    CrankComputeScore(CrankComputeScore),
+    CrankComputeDelegations(CrankComputeDelegations),
+    CrankIdle(CrankIdle),
+    CrankComputeInstantUnstake(CrankComputeInstantUnstake),
+    CrankRebalance(CrankRebalance),
+}
+
+// ---------- VIEWS ------------
+#[derive(Parser)]
+#[command(about = "View the steward state")]
+pub struct ViewState {
+    #[command(flatten)]
+    pub view_parameters: ViewParameters,
+
+    /// Views the steward state for all validators in the pool
+    #[arg(short, long)]
+    pub verbose: bool,
+}
+
+#[derive(Parser)]
+#[command(about = "View the current steward config account")]
+pub struct ViewConfig {
+    #[command(flatten)]
+    pub view_parameters: ViewParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "View the next index to remove in in the `epoch_maintenance` call")]
+pub struct ViewNextIndexToRemove {
+    #[command(flatten)]
+    pub view_parameters: ViewParameters,
+}
+
+// ---------- ACTIONS ------------
+
+#[derive(Parser)]
+#[command(about = "Initialize config account")]
+pub struct InitConfig {
+    /// Path to keypair used to pay for account creation and execute transactions
+    #[arg(short, long, env, default_value = "~/.config/solana/id.json")]
+    pub authority_keypair_path: PathBuf,
+
+    /// The current staker keypair path, defaults to the authority keypair path
+    #[arg(short, long, env)]
+    pub staker_keypair_path: Option<PathBuf>,
+
+    /// Optional path to Steward Config keypair, if not provided, a new keypair will be created
+    #[arg(long, env)]
+    pub steward_config_keypair_path: Option<PathBuf>,
+
+    /// Stake pool pubkey
+    #[arg(long, env)]
+    pub stake_pool: Pubkey,
+
+    #[command(flatten)]
+    pub transaction_parameters: TransactionParameters,
+
+    #[command(flatten)]
+    pub config_parameters: ConfigParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "Updates config account parameters")]
+pub struct UpdateConfig {
+    #[command(flatten)]
+    pub permissioned_parameters: PermissionedParameters,
+
+    #[command(flatten)]
+    pub config_parameters: ConfigParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "Initialize state account")]
+pub struct InitState {
+    #[command(flatten)]
+    pub permissioned_parameters: PermissionedParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "Reset steward state")]
+pub struct ResetState {
+    #[command(flatten)]
+    pub permissioned_parameters: PermissionedParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "Removes bad validators from the pool")]
+pub struct RemoveBadValidators {
+    #[command(flatten)]
+    pub permissioned_parameters: PermissionedParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "Calls `auto_remove_validator_from_pool`")]
+pub struct AutoRemoveValidatorFromPool {
+    #[command(flatten)]
+    pub permissionless_parameters: PermissionlessParameters,
+
+    /// Validator index of validator list to remove
+    #[arg(long, env)]
+    pub validator_index_to_remove: usize,
+}
+
+#[derive(Parser)]
+#[command(about = "Calls `auto_add_validator_from_pool`")]
+pub struct AutoAddValidatorFromPool {
+    #[command(flatten)]
+    pub permissionless_parameters: PermissionlessParameters,
+
+    /// Validator vote account to add
+    #[arg(long, env)]
+    pub vote_account: Pubkey,
+}
+
+// ---------- CRANKS ------------
+
+#[derive(Parser)]
+#[command(about = "Run epoch maintenance - needs to be run at the start of each epoch")]
+pub struct CrankEpochMaintenance {
+    #[command(flatten)]
+    pub permissionless_parameters: PermissionlessParameters,
+
+    /// Validator index to remove, gotten from `validators_to_remove` Bitmask
+    #[arg(long, env)]
+    pub validator_index_to_remove: Option<u64>,
+}
+
+#[derive(Parser)]
+#[command(about = "Crank `compute_score` state")]
+pub struct CrankComputeScore {
+    #[command(flatten)]
+    pub permissionless_parameters: PermissionlessParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "Crank `compute_delegations` state")]
+pub struct CrankComputeDelegations {
+    #[command(flatten)]
+    pub permissionless_parameters: PermissionlessParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "Crank `idle` state")]
+pub struct CrankIdle {
+    #[command(flatten)]
+    pub permissionless_parameters: PermissionlessParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "Crank `compute_instant_unstake` state")]
+pub struct CrankComputeInstantUnstake {
+    #[command(flatten)]
+    pub permissionless_parameters: PermissionlessParameters,
+}
+
+#[derive(Parser)]
+#[command(about = "Crank `rebalance` state")]
+pub struct CrankRebalance {
+    #[command(flatten)]
+    pub permissionless_parameters: PermissionlessParameters,
+}
diff --git a/utils/steward-cli/src/commands/cranks/compute_delegations.rs b/utils/steward-cli/src/commands/cranks/compute_delegations.rs
new file mode 100644
index 00000000..ab2fa739
--- /dev/null
+++ b/utils/steward-cli/src/commands/cranks/compute_delegations.rs
@@ -0,0 +1,78 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+use jito_steward::StewardStateEnum;
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+
+use solana_sdk::{
+    pubkey::Pubkey, signature::read_keypair_file, signer::Signer, transaction::Transaction,
+};
+
+use crate::{
+    commands::command_args::CrankComputeDelegations,
+    utils::{accounts::get_all_steward_accounts, transactions::configure_instruction},
+};
+
+pub async fn command_crank_compute_delegations(
+    args: CrankComputeDelegations,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    let args = args.permissionless_parameters;
+
+    // Creates config account
+    let payer =
+        read_keypair_file(args.payer_keypair_path).expect("Failed reading keypair file ( Payer )");
+
+    let steward_config = args.steward_config;
+
+    let steward_accounts = get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    match steward_accounts.state_account.state.state_tag {
+        StewardStateEnum::ComputeDelegations => { /* Continue */ }
+        _ => {
+            println!(
+                "State account is not in Compute Delegation state: {}",
+                steward_accounts.state_account.state.state_tag
+            );
+            return Ok(());
+        }
+    }
+
+    let ix = Instruction {
+        program_id,
+        accounts: jito_steward::accounts::ComputeDelegations {
+            config: steward_config,
+            state_account: steward_accounts.state_address,
+            validator_list: steward_accounts.validator_list_address,
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::ComputeDelegations {}.data(),
+    };
+
+    let blockhash = client.get_latest_blockhash().await?;
+
+    let configured_ix = configure_instruction(
+        &[ix],
+        args.transaction_parameters.priority_fee,
+        args.transaction_parameters.compute_limit,
+        args.transaction_parameters.heap_size,
+    );
+
+    let transaction = Transaction::new_signed_with_payer(
+        &configured_ix,
+        Some(&payer.pubkey()),
+        &[&payer],
+        blockhash,
+    );
+
+    let signature = client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await?;
+
+    println!("Signature: {}", signature);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/cranks/compute_instant_unstake.rs b/utils/steward-cli/src/commands/cranks/compute_instant_unstake.rs
new file mode 100644
index 00000000..98921476
--- /dev/null
+++ b/utils/steward-cli/src/commands/cranks/compute_instant_unstake.rs
@@ -0,0 +1,118 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+use jito_steward::StewardStateEnum;
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+use validator_history::id as validator_history_id;
+
+use solana_sdk::{pubkey::Pubkey, signature::read_keypair_file};
+
+use crate::{
+    commands::command_args::CrankComputeInstantUnstake,
+    utils::{
+        accounts::{
+            get_all_steward_accounts, get_cluster_history_address, get_validator_history_address,
+        },
+        transactions::{package_instructions, submit_packaged_transactions},
+    },
+};
+
+pub async fn command_crank_compute_instant_unstake(
+    args: CrankComputeInstantUnstake,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    // Creates config account
+    let payer = Arc::new(
+        read_keypair_file(args.permissionless_parameters.payer_keypair_path)
+            .expect("Failed reading keypair file ( Payer )"),
+    );
+
+    let validator_history_program_id = validator_history_id();
+    let steward_config = args.permissionless_parameters.steward_config;
+
+    let steward_accounts = get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    match steward_accounts.state_account.state.state_tag {
+        StewardStateEnum::ComputeInstantUnstake => { /* Continue */ }
+        _ => {
+            println!(
+                "State account is not in Compute Instant Unstake state: {}",
+                steward_accounts.state_account.state.state_tag
+            );
+            return Ok(());
+        }
+    }
+
+    let validators_to_run = (0..steward_accounts.state_account.state.num_pool_validators as usize)
+        .filter_map(|validator_index| {
+            let has_been_scored = steward_accounts
+                .state_account
+                .state
+                .progress
+                .get(validator_index)
+                .expect("Index is not in progress bitmask");
+            if has_been_scored {
+                None
+            } else {
+                let vote_account = steward_accounts.validator_list_account.validators
+                    [validator_index]
+                    .vote_account_address;
+                let history_account =
+                    get_validator_history_address(&vote_account, &validator_history_program_id);
+
+                Some((validator_index, vote_account, history_account))
+            }
+        })
+        .collect::<Vec<(usize, Pubkey, Pubkey)>>();
+
+    let cluster_history = get_cluster_history_address(&validator_history_program_id);
+
+    let ixs_to_run = validators_to_run
+        .iter()
+        .map(|(validator_index, _, history_account)| Instruction {
+            program_id,
+            accounts: jito_steward::accounts::ComputeInstantUnstake {
+                config: steward_config,
+                state_account: steward_accounts.state_address,
+                validator_history: *history_account,
+                validator_list: steward_accounts.validator_list_address,
+                cluster_history,
+            }
+            .to_account_metas(None),
+            data: jito_steward::instruction::ComputeInstantUnstake {
+                validator_list_index: *validator_index as u64,
+            }
+            .data(),
+        })
+        .collect::<Vec<Instruction>>();
+
+    let txs_to_run = package_instructions(
+        &ixs_to_run,
+        args.permissionless_parameters
+            .transaction_parameters
+            .chunk_size
+            .unwrap_or(15),
+        args.permissionless_parameters
+            .transaction_parameters
+            .priority_fee,
+        args.permissionless_parameters
+            .transaction_parameters
+            .compute_limit
+            .or(Some(1_400_000)),
+        args.permissionless_parameters
+            .transaction_parameters
+            .heap_size,
+    );
+
+    println!("Submitting {} instructions", ixs_to_run.len());
+    println!("Submitting {} transactions", txs_to_run.len());
+
+    let submit_stats = submit_packaged_transactions(client, txs_to_run, &payer, None, None).await?;
+
+    println!("Submit stats: {:?}", submit_stats);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/cranks/compute_score.rs b/utils/steward-cli/src/commands/cranks/compute_score.rs
new file mode 100644
index 00000000..8d2fc086
--- /dev/null
+++ b/utils/steward-cli/src/commands/cranks/compute_score.rs
@@ -0,0 +1,125 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+use jito_steward::StewardStateEnum;
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+use validator_history::id as validator_history_id;
+
+use solana_sdk::{pubkey::Pubkey, signature::read_keypair_file};
+
+use crate::{
+    commands::command_args::CrankComputeScore,
+    utils::{
+        accounts::{
+            get_all_steward_accounts, get_cluster_history_address, get_validator_history_address,
+        },
+        transactions::{package_instructions, submit_packaged_transactions},
+    },
+};
+
+pub async fn command_crank_compute_score(
+    args: CrankComputeScore,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    // Creates config account
+    let payer = Arc::new(
+        read_keypair_file(args.permissionless_parameters.payer_keypair_path)
+            .expect("Failed reading keypair file ( Payer )"),
+    );
+
+    let validator_history_program_id = validator_history_id();
+    let steward_config = args.permissionless_parameters.steward_config;
+
+    let steward_accounts = get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    match steward_accounts.state_account.state.state_tag {
+        StewardStateEnum::ComputeScores => { /* Continue */ }
+        _ => {
+            println!(
+                "State account is not in ComputeScores state: {}",
+                steward_accounts.state_account.state.state_tag
+            );
+            return Ok(());
+        }
+    }
+
+    let validators_to_run = (0..steward_accounts.state_account.state.num_pool_validators as usize)
+        .filter_map(|validator_index| {
+            let has_been_scored = steward_accounts
+                .state_account
+                .state
+                .progress
+                .get(validator_index)
+                .expect("Index is not in progress bitmask");
+            if has_been_scored {
+                None
+            } else {
+                let vote_account = steward_accounts.validator_list_account.validators
+                    [validator_index]
+                    .vote_account_address;
+                let history_account =
+                    get_validator_history_address(&vote_account, &validator_history_program_id);
+
+                Some((validator_index, vote_account, history_account))
+            }
+        })
+        .collect::<Vec<(usize, Pubkey, Pubkey)>>();
+
+    let cluster_history = get_cluster_history_address(&validator_history_program_id);
+
+    let ixs_to_run = validators_to_run
+        .iter()
+        .map(|(validator_index, vote_account, history_account)| {
+            println!(
+                "index: {}, vote_account: {}, history_account: {}\n",
+                validator_index, vote_account, history_account
+            );
+
+            Instruction {
+                program_id,
+                accounts: jito_steward::accounts::ComputeScore {
+                    config: steward_config,
+                    state_account: steward_accounts.state_address,
+                    validator_history: *history_account,
+                    validator_list: steward_accounts.validator_list_address,
+                    cluster_history,
+                }
+                .to_account_metas(None),
+                data: jito_steward::instruction::ComputeScore {
+                    validator_list_index: *validator_index as u64,
+                }
+                .data(),
+            }
+        })
+        .collect::<Vec<Instruction>>();
+
+    let txs_to_run = package_instructions(
+        &ixs_to_run,
+        args.permissionless_parameters
+            .transaction_parameters
+            .chunk_size
+            .unwrap_or(11),
+        args.permissionless_parameters
+            .transaction_parameters
+            .priority_fee,
+        args.permissionless_parameters
+            .transaction_parameters
+            .compute_limit
+            .or(Some(1_400_000)),
+        args.permissionless_parameters
+            .transaction_parameters
+            .heap_size,
+    );
+
+    println!("Submitting {} instructions", ixs_to_run.len());
+    println!("Submitting {} transactions", txs_to_run.len());
+
+    let submit_stats = submit_packaged_transactions(client, txs_to_run, &payer, None, None).await?;
+
+    println!("Submit stats: {:?}", submit_stats);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/cranks/epoch_maintenance.rs b/utils/steward-cli/src/commands/cranks/epoch_maintenance.rs
new file mode 100644
index 00000000..e5a01644
--- /dev/null
+++ b/utils/steward-cli/src/commands/cranks/epoch_maintenance.rs
@@ -0,0 +1,79 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+
+use solana_sdk::{
+    pubkey::Pubkey, signature::read_keypair_file, signer::Signer, transaction::Transaction,
+};
+
+use crate::{
+    commands::command_args::CrankEpochMaintenance,
+    utils::{accounts::get_all_steward_accounts, transactions::configure_instruction},
+};
+
+pub async fn command_crank_epoch_maintenance(
+    args: CrankEpochMaintenance,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    let validator_index_to_remove = args.validator_index_to_remove;
+    let args = args.permissionless_parameters;
+
+    // Creates config account
+    let payer =
+        read_keypair_file(args.payer_keypair_path).expect("Failed reading keypair file ( Payer )");
+
+    let steward_config = args.steward_config;
+
+    let all_steward_accounts =
+        get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    let epoch = client.get_epoch_info().await?.epoch;
+
+    if epoch == all_steward_accounts.state_account.state.current_epoch {
+        println!("Epoch is the same as the current epoch: {}", epoch);
+        return Ok(());
+    }
+
+    let ix = Instruction {
+        program_id,
+        accounts: jito_steward::accounts::EpochMaintenance {
+            config: steward_config,
+            state_account: all_steward_accounts.state_address,
+            validator_list: all_steward_accounts.validator_list_address,
+            stake_pool: all_steward_accounts.stake_pool_address,
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::EpochMaintenance {
+            validator_index_to_remove,
+        }
+        .data(),
+    };
+
+    let blockhash = client.get_latest_blockhash().await?;
+
+    let configured_ix = configure_instruction(
+        &[ix],
+        args.transaction_parameters.priority_fee,
+        args.transaction_parameters.compute_limit,
+        args.transaction_parameters.heap_size,
+    );
+
+    let transaction = Transaction::new_signed_with_payer(
+        &configured_ix,
+        Some(&payer.pubkey()),
+        &[&payer],
+        blockhash,
+    );
+
+    let signature = client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await?;
+
+    println!("Signature: {}", signature);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/cranks/idle.rs b/utils/steward-cli/src/commands/cranks/idle.rs
new file mode 100644
index 00000000..6289667b
--- /dev/null
+++ b/utils/steward-cli/src/commands/cranks/idle.rs
@@ -0,0 +1,64 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+use jito_steward::StewardStateEnum;
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+
+use solana_sdk::{
+    pubkey::Pubkey, signature::read_keypair_file, signer::Signer, transaction::Transaction,
+};
+
+use crate::{commands::command_args::CrankIdle, utils::accounts::get_all_steward_accounts};
+
+pub async fn command_crank_idle(
+    args: CrankIdle,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    let args = args.permissionless_parameters;
+
+    // Creates config account
+    let payer =
+        read_keypair_file(args.payer_keypair_path).expect("Failed reading keypair file ( Payer )");
+
+    let steward_config = args.steward_config;
+
+    let steward_accounts = get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    match steward_accounts.state_account.state.state_tag {
+        StewardStateEnum::Idle => { /* Continue */ }
+        _ => {
+            println!(
+                "State account is not in Idle state: {}",
+                steward_accounts.state_account.state.state_tag
+            );
+            return Ok(());
+        }
+    }
+
+    let ix = Instruction {
+        program_id,
+        accounts: jito_steward::accounts::Idle {
+            config: steward_config,
+            state_account: steward_accounts.state_address,
+            validator_list: steward_accounts.validator_list_address,
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::Idle {}.data(),
+    };
+
+    let blockhash = client.get_latest_blockhash().await?;
+
+    let transaction =
+        Transaction::new_signed_with_payer(&[ix], Some(&payer.pubkey()), &[&payer], blockhash);
+
+    let signature = client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await?;
+
+    println!("Signature: {}", signature);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/cranks/mod.rs b/utils/steward-cli/src/commands/cranks/mod.rs
new file mode 100644
index 00000000..5303e43e
--- /dev/null
+++ b/utils/steward-cli/src/commands/cranks/mod.rs
@@ -0,0 +1,6 @@
+pub mod compute_delegations;
+pub mod compute_instant_unstake;
+pub mod compute_score;
+pub mod epoch_maintenance;
+pub mod idle;
+pub mod rebalance;
diff --git a/utils/steward-cli/src/commands/cranks/rebalance.rs b/utils/steward-cli/src/commands/cranks/rebalance.rs
new file mode 100644
index 00000000..308260ee
--- /dev/null
+++ b/utils/steward-cli/src/commands/cranks/rebalance.rs
@@ -0,0 +1,144 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+use jito_steward::StewardStateEnum;
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+use spl_stake_pool::{find_stake_program_address, find_transient_stake_program_address};
+use validator_history::id as validator_history_id;
+
+use solana_sdk::{pubkey::Pubkey, signature::read_keypair_file, stake, system_program};
+
+use crate::{
+    commands::command_args::CrankRebalance,
+    utils::{
+        accounts::{get_all_steward_accounts, get_validator_history_address},
+        transactions::{package_instructions, submit_packaged_transactions},
+    },
+};
+
+pub async fn command_crank_rebalance(
+    args: CrankRebalance,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    // Creates config account
+    let payer = Arc::new(
+        read_keypair_file(args.permissionless_parameters.payer_keypair_path)
+            .expect("Failed reading keypair file ( Payer )"),
+    );
+
+    let validator_history_program_id = validator_history_id();
+    let steward_config = args.permissionless_parameters.steward_config;
+
+    let steward_accounts = get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    match steward_accounts.state_account.state.state_tag {
+        StewardStateEnum::Rebalance => { /* Continue */ }
+        _ => {
+            println!(
+                "State account is not in Rebalance state: {}",
+                steward_accounts.state_account.state.state_tag
+            );
+            return Ok(());
+        }
+    }
+
+    let validators_to_run = (0..steward_accounts.state_account.state.num_pool_validators as usize)
+        .filter_map(|validator_index| {
+            let has_been_rebalanced = steward_accounts
+                .state_account
+                .state
+                .progress
+                .get(validator_index)
+                .expect("Index is not in progress bitmask");
+            if has_been_rebalanced {
+                None
+            } else {
+                let vote_account = steward_accounts.validator_list_account.validators
+                    [validator_index]
+                    .vote_account_address;
+                let history_account =
+                    get_validator_history_address(&vote_account, &validator_history_program_id);
+
+                Some((validator_index, vote_account, history_account))
+            }
+        })
+        .collect::<Vec<(usize, Pubkey, Pubkey)>>();
+
+    let ixs_to_run = validators_to_run
+        .iter()
+        .map(|(validator_index, vote_account, history_account)| {
+            println!("vote_account ({}): {}", validator_index, vote_account);
+
+            let (stake_address, _) = find_stake_program_address(
+                &spl_stake_pool::id(),
+                vote_account,
+                &steward_accounts.stake_pool_address,
+                None,
+            );
+
+            let (transient_stake_address, _) = find_transient_stake_program_address(
+                &spl_stake_pool::id(),
+                vote_account,
+                &steward_accounts.stake_pool_address,
+                steward_accounts.validator_list_account.validators[*validator_index]
+                    .transient_seed_suffix
+                    .into(),
+            );
+            Instruction {
+                program_id,
+                accounts: jito_steward::accounts::Rebalance {
+                    config: steward_config,
+                    state_account: steward_accounts.state_address,
+                    validator_history: *history_account,
+                    stake_pool_program: spl_stake_pool::id(),
+                    stake_pool: steward_accounts.stake_pool_address,
+                    withdraw_authority: steward_accounts.stake_pool_withdraw_authority,
+                    validator_list: steward_accounts.validator_list_address,
+                    reserve_stake: steward_accounts.stake_pool_account.reserve_stake,
+                    stake_account: stake_address,
+                    transient_stake_account: transient_stake_address,
+                    vote_account: *vote_account,
+                    system_program: system_program::id(),
+                    stake_program: stake::program::id(),
+                    rent: solana_sdk::sysvar::rent::id(),
+                    clock: solana_sdk::sysvar::clock::id(),
+                    stake_history: solana_sdk::sysvar::stake_history::id(),
+                    stake_config: stake::config::ID,
+                }
+                .to_account_metas(None),
+                data: jito_steward::instruction::Rebalance {
+                    validator_list_index: *validator_index as u64,
+                }
+                .data(),
+            }
+        })
+        .collect::<Vec<Instruction>>();
+
+    let txs_to_run = package_instructions(
+        &ixs_to_run,
+        args.permissionless_parameters
+            .transaction_parameters
+            .chunk_size
+            .unwrap_or(1),
+        args.permissionless_parameters
+            .transaction_parameters
+            .priority_fee,
+        args.permissionless_parameters
+            .transaction_parameters
+            .compute_limit
+            .or(Some(1_400_000)),
+        None,
+    );
+
+    println!("Submitting {} instructions", ixs_to_run.len());
+    println!("Submitting {} transactions", txs_to_run.len());
+
+    let submit_stats = submit_packaged_transactions(client, txs_to_run, &payer, None, None).await?;
+
+    println!("Submit stats: {:?}", submit_stats);
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/info/mod.rs b/utils/steward-cli/src/commands/info/mod.rs
new file mode 100644
index 00000000..c4cb3b3b
--- /dev/null
+++ b/utils/steward-cli/src/commands/info/mod.rs
@@ -0,0 +1,3 @@
+pub mod view_config;
+pub mod view_next_index_to_remove;
+pub mod view_state;
diff --git a/utils/steward-cli/src/commands/info/view_config.rs b/utils/steward-cli/src/commands/info/view_config.rs
new file mode 100644
index 00000000..f1fb389c
--- /dev/null
+++ b/utils/steward-cli/src/commands/info/view_config.rs
@@ -0,0 +1,133 @@
+use std::sync::Arc;
+
+use anyhow::Result;
+use jito_steward::Config;
+use solana_client::nonblocking::rpc_client::RpcClient;
+
+use solana_sdk::pubkey::Pubkey;
+
+use crate::{
+    commands::command_args::ViewConfig,
+    utils::accounts::{get_steward_config_account, get_steward_state_address},
+};
+
+pub async fn command_view_config(
+    args: ViewConfig,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    let steward_config = args.view_parameters.steward_config;
+
+    let steward_config_account = get_steward_config_account(client, &steward_config).await?;
+    let steward_state = get_steward_state_address(&program_id, &steward_config);
+
+    // let mut output = String::new(); // Initialize the string directly
+    _print_default_config(&steward_config, &steward_state, &steward_config_account);
+
+    Ok(())
+}
+
+fn _print_default_config(steward_config: &Pubkey, steward_state: &Pubkey, config_account: &Config) {
+    let mut formatted_string = String::new();
+
+    formatted_string += "------- Config -------\n";
+    formatted_string += "šŸ“š Accounts šŸ“š\n";
+    formatted_string += &format!("Config:      {}\n", steward_config);
+    formatted_string += &format!("Admin:   {}\n", config_account.admin);
+    formatted_string += &format!("Blacklist Auth:   {}\n", config_account.blacklist_authority);
+    formatted_string += &format!(
+        "Parameter Auth:   {}\n",
+        config_account.parameters_authority
+    );
+    formatted_string += &format!("Staker (State):      {}\n", steward_state);
+    formatted_string += &format!("State:       {}\n", steward_state);
+    formatted_string += &format!("Stake Pool:  {}\n", config_account.stake_pool);
+    formatted_string += "\nā†ŗ State ā†ŗ\n";
+    formatted_string += &format!("Is Paused:   {:?}\n", config_account.paused);
+    formatted_string += &format!(
+        "Blacklisted: {:?}\n",
+        config_account.validator_history_blacklist.count()
+    );
+    formatted_string += "\nāš™ļø Parameters āš™ļø\n";
+    formatted_string += &format!(
+        "Commission Range:  {:?}\n",
+        config_account.parameters.commission_range
+    );
+    formatted_string += &format!(
+        "MEV Commission Range:  {:?}\n",
+        config_account.parameters.mev_commission_range
+    );
+    formatted_string += &format!(
+        "Epoch Credits Range:  {:?}\n",
+        config_account.parameters.epoch_credits_range
+    );
+    formatted_string += &format!(
+        "MEV Commission BPS Threshold:  {:?}\n",
+        config_account.parameters.mev_commission_bps_threshold
+    );
+    formatted_string += &format!(
+        "Scoring Delinquency Threshold Ratio:  {:?}\n",
+        config_account
+            .parameters
+            .scoring_delinquency_threshold_ratio
+    );
+    formatted_string += &format!(
+        "Instant Unstake Delinquency Threshold Ratio:  {:?}\n",
+        config_account
+            .parameters
+            .instant_unstake_delinquency_threshold_ratio
+    );
+    formatted_string += &format!(
+        "Commission Threshold:  {:?}\n",
+        config_account.parameters.commission_threshold
+    );
+    formatted_string += &format!(
+        "Historical Commission Threshold:  {:?}\n",
+        config_account.parameters.historical_commission_threshold
+    );
+    formatted_string += &format!(
+        "Number of Delegation Validators:  {:?}\n",
+        config_account.parameters.num_delegation_validators
+    );
+    formatted_string += &format!(
+        "Scoring Unstake Cap BPS:  {:?}\n",
+        config_account.parameters.scoring_unstake_cap_bps
+    );
+    formatted_string += &format!(
+        "Instant Unstake Cap BPS:  {:?}\n",
+        config_account.parameters.instant_unstake_cap_bps
+    );
+    formatted_string += &format!(
+        "Stake Deposit Unstake Cap BPS:  {:?}\n",
+        config_account.parameters.stake_deposit_unstake_cap_bps
+    );
+    formatted_string += &format!(
+        "Compute Score Slot Range:  {:?}\n",
+        config_account.parameters.compute_score_slot_range
+    );
+    formatted_string += &format!(
+        "Instant Unstake Epoch Progress:  {:?}\n",
+        config_account.parameters.instant_unstake_epoch_progress
+    );
+    formatted_string += &format!(
+        "Instant Unstake Inputs Epoch Progress:  {:?}\n",
+        config_account
+            .parameters
+            .instant_unstake_inputs_epoch_progress
+    );
+    formatted_string += &format!(
+        "Number of Epochs Between Scoring:  {:?}\n",
+        config_account.parameters.num_epochs_between_scoring
+    );
+    formatted_string += &format!(
+        "Minimum Stake Lamports:  {:?}\n",
+        config_account.parameters.minimum_stake_lamports
+    );
+    formatted_string += &format!(
+        "Minimum Voting Epochs:  {:?}\n",
+        config_account.parameters.minimum_voting_epochs
+    );
+    formatted_string += "---------------------";
+
+    println!("{}", formatted_string)
+}
diff --git a/utils/steward-cli/src/commands/info/view_next_index_to_remove.rs b/utils/steward-cli/src/commands/info/view_next_index_to_remove.rs
new file mode 100644
index 00000000..97bbc383
--- /dev/null
+++ b/utils/steward-cli/src/commands/info/view_next_index_to_remove.rs
@@ -0,0 +1,43 @@
+use std::sync::Arc;
+
+use anyhow::Result;
+use solana_client::nonblocking::rpc_client::RpcClient;
+
+use solana_sdk::pubkey::Pubkey;
+
+use crate::{
+    commands::command_args::ViewNextIndexToRemove,
+    utils::accounts::{get_all_steward_accounts, UsefulStewardAccounts},
+};
+
+pub async fn command_view_next_index_to_remove(
+    args: ViewNextIndexToRemove,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    let all_steward_accounts =
+        get_all_steward_accounts(client, &program_id, &args.view_parameters.steward_config).await?;
+
+    _print_next_index_to_remove(&all_steward_accounts);
+
+    Ok(())
+}
+
+fn _print_next_index_to_remove(steward_state_accounts: &UsefulStewardAccounts) {
+    for i in 0..steward_state_accounts
+        .state_account
+        .state
+        .num_pool_validators as usize
+    {
+        let value = steward_state_accounts
+            .state_account
+            .state
+            .validators_to_remove
+            .get_unsafe(i);
+
+        if value {
+            println!("Validator {} is marked for removal", i);
+            return;
+        }
+    }
+}
diff --git a/utils/steward-cli/src/commands/info/view_state.rs b/utils/steward-cli/src/commands/info/view_state.rs
new file mode 100644
index 00000000..291ff3a0
--- /dev/null
+++ b/utils/steward-cli/src/commands/info/view_state.rs
@@ -0,0 +1,173 @@
+use std::sync::Arc;
+
+use anyhow::Result;
+use jito_steward::StewardStateAccount;
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_sdk::pubkey::Pubkey;
+use spl_stake_pool::{find_stake_program_address, find_transient_stake_program_address};
+
+use crate::{
+    commands::command_args::ViewState,
+    utils::accounts::{get_all_steward_accounts, UsefulStewardAccounts},
+};
+
+pub async fn command_view_state(
+    args: ViewState,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    let steward_config = args.view_parameters.steward_config;
+
+    let steward_state_accounts =
+        get_all_steward_accounts(client, &program_id, &steward_config).await?;
+
+    if args.verbose {
+        _print_verbose_state(&steward_state_accounts);
+    } else {
+        _print_default_state(
+            &steward_config,
+            &steward_state_accounts.state_address,
+            &steward_state_accounts.state_account,
+        );
+    }
+
+    Ok(())
+}
+
+fn _print_verbose_state(steward_state_accounts: &UsefulStewardAccounts) {
+    let mut formatted_string;
+
+    for (index, validator) in steward_state_accounts
+        .validator_list_account
+        .validators
+        .iter()
+        .enumerate()
+    {
+        let vote_account = validator.vote_account_address;
+        let (stake_address, _) = find_stake_program_address(
+            &spl_stake_pool::id(),
+            &vote_account,
+            &steward_state_accounts.stake_pool_address,
+            None,
+        );
+
+        let (transient_stake_address, _) = find_transient_stake_program_address(
+            &spl_stake_pool::id(),
+            &vote_account,
+            &steward_state_accounts.stake_pool_address,
+            validator.transient_seed_suffix.into(),
+        );
+
+        let score_index = steward_state_accounts
+            .state_account
+            .state
+            .sorted_score_indices
+            .iter()
+            .position(|&i| i == index as u16);
+        let yield_score_index = steward_state_accounts
+            .state_account
+            .state
+            .sorted_yield_score_indices
+            .iter()
+            .position(|&i| i == index as u16);
+
+        formatted_string = String::new();
+
+        formatted_string += &format!("Vote Account: {:?}\n", vote_account);
+        formatted_string += &format!("Stake Account: {:?}\n", stake_address);
+        formatted_string += &format!("Transient Stake Account: {:?}\n", transient_stake_address);
+        formatted_string += &format!(
+            "Validator Lamports: {:?}\n",
+            u64::from(validator.active_stake_lamports)
+        );
+        formatted_string += &format!("Index: {:?}\n", index);
+        formatted_string += &format!(
+            "Is Instant Unstake: {:?}\n",
+            steward_state_accounts
+                .state_account
+                .state
+                .instant_unstake
+                .get(index)
+        );
+        formatted_string += &format!(
+            "Score: {:?}\n",
+            steward_state_accounts.state_account.state.scores.get(index)
+        );
+        formatted_string += &format!(
+            "Yield Score: {:?}\n",
+            steward_state_accounts
+                .state_account
+                .state
+                .yield_scores
+                .get(index)
+        );
+        formatted_string += &format!("Score Index: {:?}\n", score_index);
+        formatted_string += &format!("Yield Score Index: {:?}\n", yield_score_index);
+
+        println!("{}", formatted_string);
+    }
+}
+
+fn _print_default_state(
+    steward_config: &Pubkey,
+    steward_state: &Pubkey,
+    state_account: &StewardStateAccount,
+) {
+    let state = &state_account.state;
+
+    let mut formatted_string = String::new();
+
+    formatted_string += "------- State -------\n";
+    formatted_string += "šŸ“š Accounts šŸ“š\n";
+    formatted_string += &format!("Config:      {}\n", steward_config);
+    formatted_string += &format!("State:       {}\n", steward_state);
+    formatted_string += "\n";
+    formatted_string += "ā†ŗ State ā†ŗ\n";
+    formatted_string += &format!("State Tag: {}\n", state.state_tag);
+    formatted_string += &format!(
+        "Progress: {:?} / {} ({} remaining)\n",
+        state.progress.count(),
+        state.num_pool_validators,
+        state.num_pool_validators - state.progress.count() as u64
+    );
+    formatted_string += &format!(
+        "Validator Lamport Balances Count: {}\n",
+        state.validator_lamport_balances.len()
+    );
+    formatted_string += &format!("Scores Count: {}\n", state.scores.len());
+    formatted_string += &format!(
+        "Sorted Score Indices Count: {}\n",
+        state.sorted_score_indices.len()
+    );
+    formatted_string += &format!("Yield Scores Count: {}\n", state.yield_scores.len());
+    formatted_string += &format!(
+        "Sorted Yield Score Indices Count: {}\n",
+        state.sorted_yield_score_indices.len()
+    );
+    formatted_string += &format!("Delegations Count: {}\n", state.delegations.len());
+    formatted_string += &format!("Instant Unstake: {:?}\n", state.instant_unstake.count());
+    formatted_string += &format!(
+        "Progress: {:?} / {} ( {} left )\n",
+        state.progress.count(),
+        state.num_pool_validators,
+        state.num_pool_validators - state.progress.count() as u64
+    );
+    formatted_string += &format!(
+        "Start Computing Scores Slot: {}\n",
+        state.start_computing_scores_slot
+    );
+    formatted_string += &format!("Current Epoch: {}\n", state.current_epoch);
+    formatted_string += &format!("Next Cycle Epoch: {}\n", state.next_cycle_epoch);
+    formatted_string += &format!("Number of Pool Validators: {}\n", state.num_pool_validators);
+    formatted_string += &format!("Scoring Unstake Total: {}\n", state.scoring_unstake_total);
+    formatted_string += &format!("Instant Unstake Total: {}\n", state.instant_unstake_total);
+    formatted_string += &format!(
+        "Stake Deposit Unstake Total: {}\n",
+        state.stake_deposit_unstake_total
+    );
+
+    formatted_string += &format!("Padding0 Length: {}\n", state._padding0.len());
+    formatted_string += "---------------------";
+
+    println!("{}", formatted_string)
+}
diff --git a/utils/steward-cli/src/commands/init/init_state.rs b/utils/steward-cli/src/commands/init/init_state.rs
new file mode 100644
index 00000000..f347a815
--- /dev/null
+++ b/utils/steward-cli/src/commands/init/init_state.rs
@@ -0,0 +1,151 @@
+use std::sync::Arc;
+
+use anchor_lang::{AccountDeserialize, InstructionData, ToAccountMetas};
+use anyhow::Result;
+use jito_steward::{constants::MAX_ALLOC_BYTES, StewardStateAccount};
+use solana_client::nonblocking::rpc_client::RpcClient;
+
+use solana_program::instruction::Instruction;
+use solana_sdk::{
+    pubkey::Pubkey,
+    signature::{read_keypair_file, Keypair, Signature},
+    signer::Signer,
+    transaction::Transaction,
+};
+
+use crate::{
+    commands::command_args::InitState,
+    utils::{
+        accounts::{get_stake_pool_account, get_steward_config_account, get_steward_state_address},
+        transactions::configure_instruction,
+    },
+};
+
+const REALLOCS_PER_TX: usize = 10;
+
+pub async fn command_init_state(
+    args: InitState,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    // Creates config account
+    let authority = read_keypair_file(args.permissioned_parameters.authority_keypair_path)
+        .expect("Failed reading keypair file ( Authority )");
+
+    let steward_config = args.permissioned_parameters.steward_config;
+    let steward_config_account =
+        get_steward_config_account(client, &args.permissioned_parameters.steward_config).await?;
+
+    let steward_state = get_steward_state_address(&program_id, &steward_config);
+
+    let stake_pool_account =
+        get_stake_pool_account(client, &steward_config_account.stake_pool).await?;
+
+    let validator_list = stake_pool_account.validator_list;
+
+    let steward_state_account_raw = client.get_account(&steward_state).await?;
+
+    if steward_state_account_raw.data.len() == StewardStateAccount::SIZE {
+        match StewardStateAccount::try_deserialize(&mut steward_state_account_raw.data.as_slice()) {
+            Ok(steward_state_account) => {
+                if steward_state_account.is_initialized.into() {
+                    println!("State account already exists");
+                    return Ok(());
+                }
+            }
+            Err(_) => { /* Account is not initialized, continue */ }
+        };
+    }
+
+    let data_length = steward_state_account_raw.data.len();
+    let whats_left = StewardStateAccount::SIZE - data_length.min(StewardStateAccount::SIZE);
+
+    let mut reallocs_left_to_run =
+        (whats_left.max(MAX_ALLOC_BYTES) - MAX_ALLOC_BYTES) / MAX_ALLOC_BYTES + 1;
+
+    let reallocs_to_run = reallocs_left_to_run;
+    let mut reallocs_ran = 0;
+
+    while reallocs_left_to_run > 0 {
+        let reallocs_per_transaction = reallocs_left_to_run.min(REALLOCS_PER_TX);
+
+        let signature = _realloc_x_times(
+            client,
+            &program_id,
+            &authority,
+            &steward_state,
+            &steward_config,
+            &validator_list,
+            reallocs_per_transaction,
+            args.permissioned_parameters
+                .transaction_parameters
+                .priority_fee,
+            args.permissioned_parameters
+                .transaction_parameters
+                .compute_limit,
+            args.permissioned_parameters
+                .transaction_parameters
+                .heap_size,
+        )
+        .await?;
+
+        reallocs_left_to_run -= reallocs_per_transaction;
+        reallocs_ran += reallocs_per_transaction;
+
+        println!(
+            "{}/{}: Signature: {}",
+            reallocs_ran, reallocs_to_run, signature
+        );
+    }
+
+    println!("Steward State: {}", steward_state);
+
+    Ok(())
+}
+
+#[allow(clippy::too_many_arguments)]
+async fn _realloc_x_times(
+    client: &RpcClient,
+    program_id: &Pubkey,
+    authority: &Keypair,
+    steward_state: &Pubkey,
+    steward_config: &Pubkey,
+    validator_list: &Pubkey,
+    count: usize,
+    priority_fee: Option<u64>,
+    compute_limit: Option<u32>,
+    heap_size: Option<u32>,
+) -> Result<Signature> {
+    let ixs = vec![
+        Instruction {
+            program_id: *program_id,
+            accounts: jito_steward::accounts::ReallocState {
+                state_account: *steward_state,
+                config: *steward_config,
+                validator_list: *validator_list,
+                system_program: anchor_lang::solana_program::system_program::id(),
+                signer: authority.pubkey(),
+            }
+            .to_account_metas(None),
+            data: jito_steward::instruction::ReallocState {}.data(),
+        };
+        count
+    ];
+
+    let blockhash = client.get_latest_blockhash().await?;
+
+    let configured_ixs = configure_instruction(&ixs, priority_fee, compute_limit, heap_size);
+
+    let transaction = Transaction::new_signed_with_payer(
+        &configured_ixs,
+        Some(&authority.pubkey()),
+        &[&authority],
+        blockhash,
+    );
+
+    let signature = client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await?;
+
+    Ok(signature)
+}
diff --git a/utils/steward-cli/src/commands/init/init_steward.rs b/utils/steward-cli/src/commands/init/init_steward.rs
new file mode 100644
index 00000000..44e64bfb
--- /dev/null
+++ b/utils/steward-cli/src/commands/init/init_steward.rs
@@ -0,0 +1,100 @@
+use std::sync::Arc;
+
+use anchor_lang::{InstructionData, ToAccountMetas};
+use anyhow::Result;
+use jito_steward::{derive_steward_state_address, UpdateParametersArgs};
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+
+use solana_sdk::{
+    pubkey::Pubkey,
+    signature::{read_keypair_file, Keypair},
+    signer::Signer,
+    transaction::Transaction,
+};
+
+use crate::{commands::command_args::InitConfig, utils::transactions::configure_instruction};
+
+pub async fn command_init_config(
+    args: InitConfig,
+    client: &Arc<RpcClient>,
+    program_id: Pubkey,
+) -> Result<()> {
+    // Creates config account
+    let authority = read_keypair_file(args.authority_keypair_path)
+        .expect("Failed reading keypair file ( Authority )");
+
+    let staker_keypair = {
+        if let Some(staker_keypair_path) = args.staker_keypair_path {
+            read_keypair_file(staker_keypair_path).expect("Failed reading keypair file ( Staker )")
+        } else {
+            authority.insecure_clone()
+        }
+    };
+
+    let steward_config = {
+        if let Some(steward_config_keypair_path) = args.steward_config_keypair_path {
+            read_keypair_file(steward_config_keypair_path)
+                .expect("Failed reading keypair file ( Steward Config )")
+        } else {
+            Keypair::new()
+        }
+    };
+
+    let (state_account, _) = derive_steward_state_address(&steward_config.pubkey());
+
+    let update_parameters_args: UpdateParametersArgs = args.config_parameters.into();
+
+    // Check if already created
+    match client.get_account(&steward_config.pubkey()).await {
+        Ok(config_account) => {
+            if config_account.owner == program_id {
+                println!("Config account already exists");
+                return Ok(());
+            }
+        }
+        Err(_) => { /* Account does not exist, continue */ }
+    }
+
+    let init_ix = Instruction {
+        program_id,
+        accounts: jito_steward::accounts::InitializeSteward {
+            config: steward_config.pubkey(),
+            stake_pool: args.stake_pool,
+            state_account,
+            stake_pool_program: spl_stake_pool::id(),
+            system_program: anchor_lang::solana_program::system_program::id(),
+            current_staker: staker_keypair.pubkey(),
+        }
+        .to_account_metas(None),
+        data: jito_steward::instruction::InitializeSteward {
+            update_parameters_args,
+        }
+        .data(),
+    };
+
+    let blockhash = client.get_latest_blockhash().await?;
+
+    let ixs = configure_instruction(
+        &[init_ix],
+        args.transaction_parameters.priority_fee,
+        args.transaction_parameters.compute_limit,
+        args.transaction_parameters.heap_size,
+    );
+
+    let transaction = Transaction::new_signed_with_payer(
+        &ixs,
+        Some(&authority.pubkey()),
+        &[&authority, &steward_config, &staker_keypair],
+        blockhash,
+    );
+
+    let signature = client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await?;
+
+    println!("Signature: {}", signature);
+    println!("Steward Config: {}", steward_config.pubkey());
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/commands/init/mod.rs b/utils/steward-cli/src/commands/init/mod.rs
new file mode 100644
index 00000000..d1692dc5
--- /dev/null
+++ b/utils/steward-cli/src/commands/init/mod.rs
@@ -0,0 +1,2 @@
+pub mod init_state;
+pub mod init_steward;
diff --git a/utils/steward-cli/src/commands/mod.rs b/utils/steward-cli/src/commands/mod.rs
new file mode 100644
index 00000000..4a5e08cd
--- /dev/null
+++ b/utils/steward-cli/src/commands/mod.rs
@@ -0,0 +1,5 @@
+pub mod actions;
+pub mod command_args;
+pub mod cranks;
+pub mod info;
+pub mod init;
diff --git a/utils/steward-cli/src/main.rs b/utils/steward-cli/src/main.rs
new file mode 100644
index 00000000..79e941a3
--- /dev/null
+++ b/utils/steward-cli/src/main.rs
@@ -0,0 +1,82 @@
+use anyhow::Result;
+use clap::Parser;
+use commands::{
+    actions::{
+        auto_add_validator_from_pool::command_auto_add_validator_from_pool,
+        auto_remove_validator_from_pool::command_auto_remove_validator_from_pool,
+        remove_bad_validators::command_remove_bad_validators, reset_state::command_reset_state,
+        update_config::command_update_config,
+    },
+    command_args::{Args, Commands},
+    cranks::{
+        compute_delegations::command_crank_compute_delegations,
+        compute_instant_unstake::command_crank_compute_instant_unstake,
+        compute_score::command_crank_compute_score,
+        epoch_maintenance::command_crank_epoch_maintenance, idle::command_crank_idle,
+        rebalance::command_crank_rebalance,
+    },
+    info::{
+        view_config::command_view_config,
+        view_next_index_to_remove::command_view_next_index_to_remove,
+        view_state::command_view_state,
+    },
+    init::{init_state::command_init_state, init_steward::command_init_config},
+};
+use dotenv::dotenv;
+use solana_client::nonblocking::rpc_client::RpcClient;
+use std::{sync::Arc, time::Duration};
+
+pub mod commands;
+pub mod utils;
+
+#[tokio::main]
+async fn main() -> Result<()> {
+    dotenv().ok(); // Loads in .env file
+    let args = Args::parse();
+    let client = Arc::new(RpcClient::new_with_timeout(
+        args.json_rpc_url.clone(),
+        Duration::from_secs(60),
+    ));
+    let program_id = args.program_id;
+    let _ = match args.commands {
+        // ---- Views ----
+        Commands::ViewConfig(args) => command_view_config(args, &client, program_id).await,
+        Commands::ViewState(args) => command_view_state(args, &client, program_id).await,
+        Commands::ViewNextIndexToRemove(args) => {
+            command_view_next_index_to_remove(args, &client, program_id).await
+        }
+
+        // --- Actions ---
+        Commands::InitConfig(args) => command_init_config(args, &client, program_id).await,
+        Commands::UpdateConfig(args) => command_update_config(args, &client, program_id).await,
+        Commands::InitState(args) => command_init_state(args, &client, program_id).await,
+        Commands::ResetState(args) => command_reset_state(args, &client, program_id).await,
+        Commands::AutoRemoveValidatorFromPool(args) => {
+            command_auto_remove_validator_from_pool(args, &client, program_id).await
+        }
+        Commands::AutoAddValidatorFromPool(args) => {
+            command_auto_add_validator_from_pool(args, &client, program_id).await
+        }
+        Commands::RemoveBadValidators(args) => {
+            command_remove_bad_validators(args, &client, program_id).await
+        }
+
+        // --- Cranks ---
+        Commands::CrankEpochMaintenance(args) => {
+            command_crank_epoch_maintenance(args, &client, program_id).await
+        }
+        Commands::CrankComputeScore(args) => {
+            command_crank_compute_score(args, &client, program_id).await
+        }
+        Commands::CrankComputeDelegations(args) => {
+            command_crank_compute_delegations(args, &client, program_id).await
+        }
+        Commands::CrankIdle(args) => command_crank_idle(args, &client, program_id).await,
+        Commands::CrankComputeInstantUnstake(args) => {
+            command_crank_compute_instant_unstake(args, &client, program_id).await
+        }
+        Commands::CrankRebalance(args) => command_crank_rebalance(args, &client, program_id).await,
+    };
+
+    Ok(())
+}
diff --git a/utils/steward-cli/src/utils/accounts.rs b/utils/steward-cli/src/utils/accounts.rs
new file mode 100644
index 00000000..a3cc8395
--- /dev/null
+++ b/utils/steward-cli/src/utils/accounts.rs
@@ -0,0 +1,133 @@
+use anchor_lang::AccountDeserialize;
+use anyhow::Result;
+use jito_steward::{
+    utils::{StakePool, ValidatorList},
+    Config, StewardStateAccount,
+};
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_sdk::pubkey::Pubkey;
+use spl_stake_pool::find_withdraw_authority_program_address;
+use validator_history::{ClusterHistory, ValidatorHistory};
+
+pub struct UsefulStewardAccounts {
+    pub config_account: Config,
+    pub state_account: StewardStateAccount,
+    pub state_address: Pubkey,
+    pub stake_pool_account: StakePool,
+    pub stake_pool_address: Pubkey,
+    pub stake_pool_withdraw_authority: Pubkey,
+    pub validator_list_account: ValidatorList,
+    pub validator_list_address: Pubkey,
+}
+
+pub async fn get_all_steward_accounts(
+    client: &RpcClient,
+    program_id: &Pubkey,
+    steward_config: &Pubkey,
+) -> Result<Box<UsefulStewardAccounts>> {
+    let config_account = get_steward_config_account(client, steward_config).await?;
+    let (state_account, state_address) =
+        get_steward_state_account(client, program_id, steward_config).await?;
+    let stake_pool_address = config_account.stake_pool;
+    let stake_pool_account = get_stake_pool_account(client, &stake_pool_address).await?;
+
+    let stake_pool_withdraw_authority = get_withdraw_authority_address(&stake_pool_address);
+    let validator_list_address = stake_pool_account.validator_list;
+    let validator_list_account =
+        get_validator_list_account(client, &validator_list_address).await?;
+
+    Ok(Box::new(UsefulStewardAccounts {
+        config_account,
+        state_account,
+        state_address,
+        stake_pool_account,
+        stake_pool_address,
+        stake_pool_withdraw_authority,
+        validator_list_account,
+        validator_list_address,
+    }))
+}
+
+pub async fn get_steward_config_account(
+    client: &RpcClient,
+    steward_config: &Pubkey,
+) -> Result<Config> {
+    let config_raw_account = client.get_account(steward_config).await?;
+
+    Ok(Config::try_deserialize(
+        &mut config_raw_account.data.as_slice(),
+    )?)
+}
+
+pub fn get_steward_state_address(program_id: &Pubkey, steward_config: &Pubkey) -> Pubkey {
+    let (steward_state, _) = Pubkey::find_program_address(
+        &[StewardStateAccount::SEED, steward_config.as_ref()],
+        program_id,
+    );
+
+    steward_state
+}
+
+pub async fn get_steward_state_account(
+    client: &RpcClient,
+    program_id: &Pubkey,
+    steward_config: &Pubkey,
+) -> Result<(StewardStateAccount, Pubkey)> {
+    let steward_state = get_steward_state_address(program_id, steward_config);
+
+    let state_raw_account = client.get_account(&steward_state).await?;
+    Ok((
+        StewardStateAccount::try_deserialize(&mut state_raw_account.data.as_slice())?,
+        steward_state,
+    ))
+}
+
+pub async fn get_stake_pool_account(client: &RpcClient, stake_pool: &Pubkey) -> Result<StakePool> {
+    let stake_pool_account_raw = client.get_account(stake_pool).await?;
+
+    Ok(StakePool::try_deserialize(
+        &mut stake_pool_account_raw.data.as_slice(),
+    )?)
+}
+
+pub fn get_withdraw_authority_address(stake_pool_address: &Pubkey) -> Pubkey {
+    let (withdraw_authority, _) =
+        find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address);
+
+    withdraw_authority
+}
+
+pub async fn get_validator_list_account(
+    client: &RpcClient,
+    validator_list: &Pubkey,
+) -> Result<ValidatorList> {
+    let validator_list_account_raw = client.get_account(validator_list).await?;
+
+    Ok(ValidatorList::try_deserialize(
+        &mut validator_list_account_raw.data.as_slice(),
+    )?)
+}
+
+pub fn get_cluster_history_address(validator_history_program_id: &Pubkey) -> Pubkey {
+    let (address, _) =
+        Pubkey::find_program_address(&[ClusterHistory::SEED], validator_history_program_id);
+    address
+}
+
+pub fn get_validator_history_address(
+    vote_account: &Pubkey,
+    validator_history_program_id: &Pubkey,
+) -> Pubkey {
+    let (address, _) = Pubkey::find_program_address(
+        &[ValidatorHistory::SEED, &vote_account.to_bytes()],
+        validator_history_program_id,
+    );
+
+    address
+}
+
+pub fn get_validator_history_config_address(validator_history_program_id: &Pubkey) -> Pubkey {
+    let (address, _) = Pubkey::find_program_address(&[Config::SEED], validator_history_program_id);
+
+    address
+}
diff --git a/utils/steward-cli/src/utils/mod.rs b/utils/steward-cli/src/utils/mod.rs
new file mode 100644
index 00000000..c0963f95
--- /dev/null
+++ b/utils/steward-cli/src/utils/mod.rs
@@ -0,0 +1,2 @@
+pub mod accounts;
+pub mod transactions;
diff --git a/utils/steward-cli/src/utils/transactions.rs b/utils/steward-cli/src/utils/transactions.rs
new file mode 100644
index 00000000..97becc0a
--- /dev/null
+++ b/utils/steward-cli/src/utils/transactions.rs
@@ -0,0 +1,113 @@
+use std::sync::Arc;
+
+use anyhow::Result;
+use keeper_core::{parallel_execute_transactions, SubmitStats, TransactionExecutionError};
+use solana_client::nonblocking::rpc_client::RpcClient;
+use solana_program::instruction::Instruction;
+
+use solana_sdk::{
+    compute_budget::ComputeBudgetInstruction, signature::Keypair, signer::Signer,
+    transaction::Transaction,
+};
+
+pub fn configure_instruction(
+    ixs: &[Instruction],
+    priority_fee: Option<u64>,
+    compute_limit: Option<u32>,
+    heap_size: Option<u32>,
+) -> Vec<Instruction> {
+    let mut instructions = ixs.to_vec();
+    if let Some(compute_limit) = compute_limit {
+        instructions.insert(
+            0,
+            ComputeBudgetInstruction::set_compute_unit_limit(compute_limit),
+        );
+    }
+    if let Some(priority_fee) = priority_fee {
+        instructions.insert(
+            0,
+            ComputeBudgetInstruction::set_compute_unit_price(priority_fee),
+        );
+    }
+    if let Some(heap_size) = heap_size {
+        instructions.insert(0, ComputeBudgetInstruction::request_heap_frame(heap_size));
+    }
+
+    instructions
+}
+
+pub fn package_instructions(
+    ixs: &[Instruction],
+    chunk_size: usize,
+    priority_fee: Option<u64>,
+    compute_limit: Option<u32>,
+    heap_size: Option<u32>,
+) -> Vec<Vec<Instruction>> {
+    ixs.chunks(chunk_size)
+        .map(|chunk: &[Instruction]| {
+            configure_instruction(chunk, priority_fee, compute_limit, heap_size)
+        })
+        .collect::<Vec<Vec<Instruction>>>()
+}
+
+pub async fn submit_packaged_transactions(
+    client: &Arc<RpcClient>,
+    transactions: Vec<Vec<Instruction>>,
+    keypair: &Arc<Keypair>,
+    retry_count: Option<u16>,
+    retry_interval: Option<u64>,
+) -> Result<SubmitStats, TransactionExecutionError> {
+    let mut stats = SubmitStats::default();
+    let tx_slice = transactions
+        .iter()
+        .map(|t| t.as_slice())
+        .collect::<Vec<_>>();
+
+    match parallel_execute_transactions(
+        client,
+        &tx_slice,
+        keypair,
+        retry_count.unwrap_or(3),
+        retry_interval.unwrap_or(20),
+    )
+    .await
+    {
+        Ok(results) => {
+            stats.successes = results.iter().filter(|&tx| tx.is_ok()).count() as u64;
+            stats.errors = results.len() as u64 - stats.successes;
+            stats.results = results;
+            Ok(stats)
+        }
+        Err(e) => Err(e),
+    }
+}
+
+pub async fn debug_send_single_transaction(
+    client: &Arc<RpcClient>,
+    payer: &Arc<Keypair>,
+    instructions: &[Instruction],
+    debug_print: Option<bool>,
+) -> Result<solana_sdk::signature::Signature, solana_client::client_error::ClientError> {
+    let transaction = Transaction::new_signed_with_payer(
+        instructions,
+        Some(&payer.pubkey()),
+        &[&payer],
+        client.get_latest_blockhash().await?,
+    );
+
+    let result = client.send_and_confirm_transaction(&transaction).await;
+
+    if debug_print.unwrap_or(false) {
+        match &result {
+            Ok(signature) => {
+                println!("Signature: {}", signature);
+            }
+            Err(e) => {
+                println!("Accounts: {:?}", &instructions.last().unwrap().accounts);
+                println!("Error: {:?}", e);
+            }
+        }
+    }
+
+    result
+}