> ## Documentation Index
> Fetch the complete documentation index at: https://seilabs-docs-evm-cookbook.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Giga SS Store Migration Guide

> Migrate a Sei RPC node to Giga SS Store: split EVM state into a dedicated state-store backend so non-EVM modules stop paying EVM write amplification.

Giga SS Store is the next step in Sei's storage evolution, built on [SeiDB](/node/node-operators#architecture).
It splits the hot EVM state into a dedicated state-store (SS) database.
The node can then scale toward the target throughput of approximately 150k TPS,
and non-EVM modules stop paying write amplification for EVM state.

After the migration, the SS layer is repartitioned into two cooperating stores:

| Layer | Cosmos backend | EVM backend |
| - | - | - |
| **SC** (State Commit, app hash) | `memiavl` | FlatKV |
| **SS** (State Store, historical queries) | single PebbleDB MVCC database | dedicated EVM SS MVCC databases in the configured EVM SS directory |

This migration changes only the **SS** layer. The SC layer config stays the same,
and `memiavl` remains the authoritative source for the app hash, so the change is
invisible to the network.

<Info>This guide follows the canonical procedure in the `sei-chain` repository: [`docs/migration/giga_store_migration.md`](https://github.com/sei-protocol/sei-chain/blob/main/docs/migration/giga_store_migration.md). If anything here drifts, open an issue there.</Info>

## Prerequisites

<Warning>Run this migration on **RPC nodes only**. This flow does not support validator nodes or archive nodes yet, so do not run it against either.</Warning>

* A `seid` build that supports the `evm-ss-split` flag (Sei v6.5 or later). Older
  releases used the per-key `evm-ss-write-mode` and `evm-ss-read-mode` toggles. If your
  `app.toml` still has those keys, upgrade `seid` before you continue.
* `sc-enable = true` and `ss-enable = true` in `app.toml`. Both must stay enabled.
* A trusted RPC endpoint for state sync (the chain ID and trust-height source).
* Disk headroom for two SS databases. The EVM split does not duplicate data, but
  the old and new layouts may briefly coexist on disk during the migration.

The migration **requires a full state sync**. There is no in-place migration
path and no live "dual-write then split" workflow. The state sync wipes the
local data directory and imports a fresh snapshot into the new layout.

## Benefits

* The node serves EVM reads only from a dedicated EVM SS database.
* Non-EVM modules no longer pay write amplification for EVM state.

## What's different about EVM SS

EVM SS is **point-query only by design** (`Get` and `Has`). For performance,
iteration is explicitly disabled on the EVM backend. The hot EVM read path is
tuned for direct key lookups, and cross-bucket scans would defeat the per-type
sub-DB layout. Any EVM read that needs iteration must stay on the Cosmos SS side.

## Migration steps

### Step 1: Update `app.toml`

Apply these settings in `~/.sei/config/app.toml`:

```toml copy theme={null}
[state-commit]
# State commit is untouched by this migration.
sc-enable = true

[state-store]
ss-enable = true

# Use PebbleDB for the Cosmos SS MVCC DB and every EVM SS sub-DB.
ss-backend = "pebbledb"

# Route EVM state to the dedicated EVM SS backend.
# When false (default), EVM state lives in the Cosmos SS backend alongside
# everything else. When true, EVM data is routed exclusively to the EVM SS
# backend; non-EVM data stays in Cosmos SS. No fallback between backends.
evm-ss-split = true
```

<Warning>Keep `ss-backend = "pebbledb"` during this migration. RocksDB support for the state store will be removed. No target release has been published. If the node already uses RocksDB, follow [Move off RocksDB](/node/node-operators#move-off-rocksdb).</Warning>

### Step 2: State sync into the new layout

Giga SS Store is fully compatible with the existing state-snapshot format. On
import, the composite state store routes each snapshot node based on the
importing node's `evm-ss-split`:

* With `evm-ss-split = true`, EVM snapshot nodes go only into EVM SS, and
  non-EVM nodes go only into Cosmos SS.
* The import path normalizes legacy `evm_flatkv` snapshot nodes to `evm`, so it
  accepts snapshots from either the old or the new FlatKV module.

Both stores are then fully populated at the snapshot height, so the node can
serve reads immediately.

The [state sync guide](/node/statesync) documents the full flow. The minimal
flow for this migration is:

```bash copy theme={null}
export TRUST_HEIGHT_DELTA=10000
export MONIKER="<moniker>"
export CHAIN_ID="<chain_id>"
export PRIMARY_ENDPOINT="<rpc_endpoint>"
export SEID_HOME="$HOME/.sei"

# 1. Stop seid
sudo systemctl stop seid

# 2. Back up files you need to preserve and wipe local state
cp $SEID_HOME/data/priv_validator_state.json /tmp/priv_validator_state.json
cp $SEID_HOME/config/priv_validator_key.json   /tmp/priv_validator_key.json
cp $SEID_HOME/config/genesis.json              /tmp/genesis.json
rm -rf $SEID_HOME/data/*
rm -rf $SEID_HOME/wasm
rm -rf $SEID_HOME/config/priv_validator_key.json
rm -rf $SEID_HOME/config/genesis.json
rm -rf $SEID_HOME/config/config.toml

# 3. Re-init, re-apply config.toml and app.toml (set Step 1 values again)
seid init --chain-id "$CHAIN_ID" "$MONIKER"

# 4. Resolve trust height/hash and persistent peers against PRIMARY_ENDPOINT,
#    then update config.toml. See /node/statesync for the full snippet.

# 5. Restore the backed-up files
cp /tmp/priv_validator_state.json $SEID_HOME/data/priv_validator_state.json
cp /tmp/priv_validator_key.json   $SEID_HOME/config/priv_validator_key.json
cp /tmp/genesis.json              $SEID_HOME/config/genesis.json

# 6. Start seid
sudo systemctl restart seid
```

<Warning>Make sure `priv_validator_key.json` is in safe storage before you delete it from the config directory. The loss of this key is unrecoverable for a validator but does not matter for RPC-only nodes. If you follow these steps from the wrong checklist, you can lose a validator key permanently.</Warning>

### Step 3: Verify the new layout

After the state sync completes and the node starts producing blocks, confirm
that Giga SS Store is active in two places.

**Startup logs.** All three lines should appear:

```text theme={null}
"SeiDB SS is enabled"                       # with the configured `backend`
"SeiDB EVM StateStore optimization is enabled"  # with the `separateDBs` label
"EVM state store enabled"                   # with `dir` and `separateDBs` labels
```

**EVM RPC.** `debug_traceBlockByNumber` is the cleanest end-to-end check.
It forces the node to read EVM state from the new EVM SS backend:

```bash copy theme={null}
curl -s -X POST http://127.0.0.1:8545 \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"debug_traceBlockByNumber","params":["latest",{}],"id":1}'
```

The response should contain a `"result"` field, not an RPC error.

## Safety checks

`seid` runs three DB-state checks at startup and refuses to start if the EVM SS
and Cosmos SS DBs are inconsistent. They specifically catch the mistake of
flipping `evm-ss-split` from `false` to `true` without a state sync.

1. **EVM SS directory missing or empty** (before the EVM SS is opened). This
   check applies when `evm-ss-split = true`. If Cosmos SS already has committed
   history but the configured EVM SS directory is missing or empty, the
   composite state store refuses to proceed. The check fails before the sub-DBs
   are opened, so a rejected config does not leave a confusing empty directory
   behind.
2. **EVM SS DB empty post-open, pre-recovery.** This second safeguard for check 1
   covers the case where the directory exists but its DBs are empty. The WAL
   covers only the last `KeepRecent` blocks, so replay cannot rebuild a fresh
   EVM SS from scratch.
3. **Mismatched earliest versions, post-recovery.** If the two DBs were
   populated from different snapshots (or pruned independently), historical
   reads would be inconsistent. A non-zero earliest-version divergence
   aborts startup.

If any check fails, the correct fix is to either (a) complete the state sync
described above or (b) set `evm-ss-split = false` and restart. If the configured
EVM SS directory is stale from a failed attempt, remove it before the state sync.

## Rollback

To roll back:

1. Set `evm-ss-split = false` in `app.toml`.
2. Restart the node. The EVM SS DB is no longer opened but stays on disk until
   you remove it.

To fully reclaim the disk used by EVM SS, revert the setting first. Then stop
the node and delete the configured EVM SS directory.

<Warning>To roll back cleanly to `evm-ss-split = false`, run another state sync. Under `evm-ss-split = true`, EVM writes go only to the EVM SS DB, so Cosmos SS does not have those writes. If you restart with `evm-ss-split = false`, the node stops opening the EVM SS DB. However, EVM-state queries miss anything written after the Giga state sync until you run state sync again without the split.</Warning>

## FlatKV EVM SC migration flow

Everything above concerns the **SS** (State Store) layer. The **SC** (State
Commit) layer has a separate migration path. It moves the hot `evm/` data out
of `memiavl` and into FlatKV in place, without a state sync. The `sc-write-mode`
setting in `app.toml` drives this path entirely. You coordinate it across a
quorum by stopping the nodes, editing the config, and restarting them.

Unlike the SS split, the SC-side migration **does change how `evm/` data
contributes to the app hash**. The contribution moves from the memiavl IAVL root
to the FlatKV lattice hash. Because of this, every validator in a quorum must
flip at the same coordinated stop. If a node flips while its peers are still on
the old mode, it produces a different AppHash on the next block, and consensus
halts. The safe sequence is always: stop every node, rewrite `app.toml`
everywhere, and restart every node.

<Warning>Do not run this SC-side flow against Sei Testnet or Sei Mainnet nodes unless your version's release notes explicitly list it as supported. The cluster and devnet integration harness tests this FlatKV EVM migration flow.</Warning>

### Write modes

The migration is a transition from the `memiavl_only` write mode to
`migrate_evm`. In `memiavl_only` (v0), memiavl is the sole SC backend and FlatKV
is not allocated. The in-flight mode, `migrate_evm`, drains `evm/` keys from
memiavl into FlatKV. After the migration completes, flip `sc-write-mode`
again to `evm_migrated`, so that later restarts do not start the migration
manager.

`evm_migrated` is not the last mode. Three more modes sit between it and the
terminal mode:

* `migrate_all_but_bank` drains every remaining module except `bank/` from
  memiavl into FlatKV.
* `all_migrated_but_bank` is the steady state after that drain completes.
* `migrate_bank` drains the final `bank/` module.

`flatkv_only` is the fully supported **terminal** steady-state write mode.
FlatKV is the sole SC backend, and `memiavl` is not allocated at all. FlatKV
serves the SC state of every module. State-sync snapshot export and restore
work correctly in this mode, and so does app-hash parity. A node can therefore
boot or state sync directly into the post-migration shape, without ever running
the migration manager. Such a node uses `flatkv_only`.

`flatkv_only` is valid only for a node whose modules have all been drained out
of memiavl. It is not a flip target for an `evm_migrated` node, which still
holds `bank/` and every other non-EVM module in memiavl. It becomes the valid
flip target after `migrate_bank` reports complete on the node (migration
version 3, all modules in FlatKV). Then restart with
`sc-write-mode = "flatkv_only"` to reach the terminal steady state.

<Note>A correctness bug in the WAL replay path is fixed. On replay (catchup,
read-only clone, snapshot export, and state-sync restore), the bug dropped empty
(zero-length) values written with no delete flag. This made the FlatKV state and
the consensus AppHash diverge from the live chain. Empty-value writes are now
preserved across a WAL round-trip and a state sync, which makes `flatkv_only`
state sync reliable.</Note>

While a node is in a migration mode:

* Caller reads of not-yet-migrated keys fall back to FlatKV for new keys written
  after the migration started, and to memiavl otherwise.
* A merging iterator over both backends serves iteration. It queries memiavl
  first, and FlatKV wins on ties, so range scans see the complete key set during
  a migration.
* The migration boundary advances at most once per block.

### Operator-facing knobs

**`sc-keys-to-migrate-per-block`** (`app.toml`, `[state-commit]` section)
controls how many EVM keys the in-flight migration drains from memiavl into
FlatKV per block. The default is `1024`, which is appropriate for production
drains. A lower value spreads the migration across more blocks. The value must
be `> 0`. The node ignores it entirely when `sc-write-mode` is not a migration
mode.

```toml copy theme={null}
[state-commit]
sc-write-mode = "migrate_evm"
sc-keys-to-migrate-per-block = 1024
```

**`GIGA_MIGRATE_FROM_MEMIAVL`** is a Docker cluster environment variable for the
local devnet setup. When set to `true`, it boots every node in `memiavl_only`
mode, the v0 starting point for the FlatKV EVM migrate flow. It is mutually
exclusive with `GIGA_STORAGE`. If both are set, `GIGA_MIGRATE_FROM_MEMIAVL`
takes precedence.

```bash copy theme={null}
GIGA_MIGRATE_FROM_MEMIAVL=true make docker-cluster-start
```

### Checking migration status

The `seidb migrate-evm-status` subcommand reports the on-disk FlatKV EVM
migrate state of a FlatKV directory as JSON. It clones the latest snapshot and
WAL into a temporary directory before it reads. You can therefore run it against
a live node's data directory without contending for the FlatKV writer lock.

```bash copy theme={null}
seidb migrate-evm-status --db-dir <flatkv-dir> [--height <n>]
# Full flag and JSON-field reference: /node/technical-reference (seidb Tooling Commands)
```

`--db-dir` (short `-d`) points at the FlatKV data directory. `--height` selects
a target version. The default, `0`, selects the latest available version. The
emitted JSON includes `migrate_evm_complete` (true after the migration finishes),
`migration_version`, `version_at`, and whether an in-flight boundary is still
present. Before you flip `sc-write-mode` to `evm_migrated`, poll the command
until `migrate_evm_complete` reports `true` on every validator.

When the migration finishes, each node also emits a `migration complete` summary
log line and `seidb_migration_*` OpenTelemetry counters for the migrated keys
and bytes.

### Importing EVM state from memIAVL into FlatKV

The `seidb import-flatkv-from-memiavl` command populates a FlatKV store from an existing memIAVL tree. It is for nodes that move to the FlatKV EVM commit store without a state sync. Two safety properties matter:

* The import height must equal the latest memIAVL version. The command refuses a lower height, because the composite store's version reconciliation would silently roll memIAVL back and truncate blocks. It also refuses a higher height. First, check the current version with `seidb memiavl-latest-version`. If needed, roll memIAVL back to the target height before you import.
* Overwriting existing committed FlatKV data requires the explicit `--force` flag.

## FAQ

### Where do the data files live after migrating?

* Cosmos SS data uses `data/pebbledb/` in the legacy layout and
  `data/state_store/cosmos/pebbledb/` in the current layout.
* EVM SS data uses `data/evm_ss/` in the legacy layout and
  `data/state_store/evm/pebbledb/` in the current layout.
* Nodes created before the layout change keep their legacy paths automatically.
  A legacy directory takes precedence when it is present. New nodes use the
  current layout.
* A non-empty `ss-db-directory` or `evm-ss-db-directory` overrides the
  corresponding default path.
* This migration does not change SC data (`memiavl` and FlatKV).

### Does Giga SS Store change the app hash or consensus?

No. The SC layer is unchanged, so `memiavl` remains the authoritative source
for the app hash. Giga SS Store is a per-node SS change that is invisible to
the network.

### Can I migrate a validator node with this guide?

Not yet. This migration guide is for RPC nodes only.

### Can I migrate an archive node with this guide?

Not yet. Archive-node migration is out of scope for this guide.

### Can I toggle back to `evm-ss-split = false` after enabling it?

Yes, but a clean rollback requires another state sync. See the
[Rollback](#rollback) section above.

### Why can't I just flip `evm-ss-split = true` on a running node?

The `evm-ss-split = true` setting requires the EVM SS DB to already contain the
full history that Cosmos SS has. A live flip would leave the EVM SS DB empty,
and the composite store refuses to fall back to Cosmos SS. The result would be
missing EVM state at query time. The safety checks above block this scenario at
startup.

### Does Giga SS Store support historical proofs?

No, and neither does SeiDB. SS stores raw KVs and does not reconstruct
IAVL-style proofs.

### Does enabling Giga Storage change the receipt backend?

In the `localnode` and `rpcnode` configuration scripts, setting
`GIGA_STORAGE=true` defaults `RECEIPT_BACKEND` to `pebble` unless you set
`RECEIPT_BACKEND` explicitly. To use a different value with Giga Storage, set the
`RECEIPT_BACKEND` environment variable explicitly. The explicit value takes
precedence over the default.

`pebbledb` (also called `pebble`) is now the only supported receipt-store
backend. The former `parquet` option was removed. A `RECEIPT_BACKEND=parquet`
setting (or `rs-backend = "parquet"` in `app.toml`) is rejected with an error
(`unsupported receipt-store backend "parquet"; supported: pebbledb`).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.