---
name: mev-commit-opt-in
description: >
  Opt Ethereum L1 validator pubkeys into or out of the mev-commit validator coalition
  (vanilla, EigenLayer, Symbiotic). Verify Hub status, send and confirm registry
  transactions on the operator's RPC and wallet, and compute real on-chain ETH stake.
  Coalition membership is one signal across keys, not extra bid revenue.
  Use when the user mentions validator opt-in, opt out, VanillaRegistry, MevCommitAVS,
  MevCommitMiddleware, proposer registry, BLS pubkey registration, Gnosis Safe,
  multi-sig, or /mev-commit-opt-in.
---

# mev-commit-opt-in

Register L1 validator BLS pubkeys into the **mev-commit validator coalition** by calling the published registry contracts on Ethereum. Hub-true keys are one signal across many validators: mev-forward, innovation-forward, used for Ethereum policy and later coalition fees.

This is **not** extra block revenue from a live commitment market. Do not tell the operator they now earn yield from mev-commit bids, preconfs, or a running network. Any mev-boost relay set is fine. No relay checkbox.

Canonical page: https://primev.xyz/ai
Why: https://docs.primev.xyz/v1.2.x/knowledge-base/why-should-validators-opt-in
How: https://docs.primev.xyz/v1.2.x/get-started/validators/agentic-opt-in
Addresses, signatures, errors, and stake math live in `references/`. Do not invent other contracts.

## What membership is

- **Now:** on-chain roster (ValidatorOptInHub). Coalition advocacy with the EF and Ethereum groups. Innovation review as the protocol evolves.
- **Fees:** No fees are enabled at this stage. There is no fee switch without the operator's approval. Members must actively approve any coalition fee before it starts. When a fee is approved and enabled, it funds AI research on Ethereum clients, validators, staking, and yield, including a step toward about 1% of validator yield.
- **Later (not live):** The entity can be a nonprofit. Founding members are granted tokens in proportion to their contributions, sized so token value matches or exceeds fees they contribute. The token is the right to create proposals for holders to accept.

If asked "what do I earn?", say membership, that no fee is on, and that any later fee needs their approval and then funds AI research. Do not invent a live APY or bid-revenue number.

## Safety

- Never print, log, commit, or write a private key. Prefer Foundry keystore, `cast wallet`, or hardware. Use `PRIVATE_KEY` only if the operator insists.
- Only register pubkeys the operator controls. Registering someone else's key can be slashed.
- One pubkey, one method. Do not double-register.
- If the signer is a **Safe / Gnosis / multi-sig**, do **not** `cast send`. Follow the Safe section below. Never collect owner keys.
- Simulate every write, then send on **their** `ETH_RPC_URL` (L1, chain id 1 or Hoodi 560048) **only for EOAs**. Wait for the receipt. Then verify on the Hub.
- Batch 40 to 60 keys per transaction.

## Keys

Accept `.txt`, `.csv`, paste, newlines, or commas. Normalize each entry to `0x` + 96 hex (48-byte compressed BLS). Drop empties. Reject anything else.

```text
0xa1b2...   # 98 chars including 0x
```

## Walk

1. Choose method: **vanilla** (simple ETH), **eigenlayer**, or **symbiotic**. Hoodi-only extras: Lido, Rocket Pool (link the docs path pages; do not improvise).
2. Parse keys. Confirm count and first/last pubkey with the operator.
3. Confirm signer + `ETH_RPC_URL`. Ask if this is a Safe / multi-sig. If the address has contract code, treat it as a Safe. Then follow the Safe section and skip step 4 send.
4. EOA only: simulate, send, wait for receipt, then Hub-verify every key.
5. Report: method, key count, tx hash (if any), Etherscan or Hoodi link, Hub booleans, next step. **Done means Hub `true` on every key**, not a Safe "success" banner and not a receipt alone.

No relay configuration step.

## Safe / multi-sig

A Safe cannot `cast send`. An owner key is not the Safe. Never collect owner keys.

Vanilla: the Safe is the staker (only it can unstake). ETH for `minStake * n` must sit in the Safe. EigenLayer: Safe is pod owner or delegated AVS operator. Symbiotic: Safe is the registered operator.

1. Simulate: `cast call $CONTRACT "$SIG" $ARGS --from $SAFE --rpc-url $RPC`. If it reverts, stop.
2. Encode: `cast calldata "$SIG" $ARGS`.
3. Hand them this card:

```
Safe opt-in (do not send from an owner wallet)

Open https://app.safe.global on THIS Safe.
New transaction → Transaction Builder
To: $CONTRACT
Value: $WEI wei (vanilla stake only; else 0)
Data: $CALLDATA
Operation: Call (not DelegateCall)

Propose → owners sign to threshold → Execute.
Paste the L1 tx hash back, or say "executed".
I mark done only when ValidatorOptInHub is true for every key.
```

4. **Complete = Hub, not Safe UI.** If they give an L1 hash: `cast receipt`, then still `areValidatorsOptedIn`. If they say executed: skip receipt, Hub-check. If still false, poll Hub every ~15s for a few minutes, then report which keys flipped. Queued / awaiting confirmations is not done.

## Writes

Read `references/methods.md` for exact signatures and `cast` shapes.

- Vanilla: `stake(bytes[])` payable. Read `minStake()` first. `msg.value` is split evenly. Extra ETH is a slash buffer, not prepaid capacity.
- EigenLayer: `registerValidatorsByPodOwners(bytes[][], address[])`. Caller must be the pod owner or the delegated AVS operator. Plural name only.
- Symbiotic: `registerValidators(bytes[][], address[])`. Vault and operator must already be registered. If setup is missing, stop and name the exact gap (see errors).

## Opt out

- Vanilla: `unstake(bytes[])`, wait `unstakePeriodBlocks()`, then `withdraw(bytes[])`.
- EigenLayer: `requestValidatorsDeregistration(bytes[])`, wait, then `deregisterValidators(bytes[])`.
- Symbiotic: `requestValDeregistrations(bytes[])`, wait, then `deregisterValidators(bytes[])`.

## Verify

Always use **ValidatorOptInHub** (not the legacy Router) on L1:

```
isValidatorOptedIn(bytes)(bool)
areValidatorsOptedIn(bytes[])(bool[])
```

A receipt is not enough. A Safe UI "Success" is not enough. Hub `true` is opted in.

If they used a Safe, poll Hub (`areValidatorsOptedIn` on the exact set) after they execute. If they pasted an L1 hash, check `cast receipt` first, then still Hub-check.

## Status (no performance analytics)

- How many of my keys are opted in: Hub `areValidatorsOptedIn` on the set.
- Operator / withdrawal / pod-owner: signer address, plus AVS `getOperatorRegInfo` / middleware operator views.
- Total ETH stake: follow `references/stake.md`. Sum real balances. **Never** `32 ETH × key count`. If beacon effective balance is needed, use a beacon node or beaconcha.in (`BEACONCHAIN_API_KEY`). If the key is missing, ask for it. Do not invent a placeholder.

Do not query primev-metrics-cron, relaydb, or “how are my keys performing?”

## Errors

On revert, read `references/errors.md`. Tell the operator the contract, function, revert name, the address or pubkey that failed, and the exact missing step (including when they must ping Primev, e.g. vault not registered, vanilla whitelist).

## Addresses
# Registry addresses

Call **proxies**. L1 only. Not the mev-commit chain.

## Mainnet (chain id 1)

| Contract | Proxy |
| --- | --- |
| VanillaRegistry | `0x47afdcB2B089C16CEe354811EA1Bbe0DB7c335E9` |
| MevCommitAVS | `0xBc77233855e3274E1903771675Eb71E602D9DC2e` |
| MevCommitMiddleware | `0x21fD239311B050bbeE7F32850d99ADc224761382` |
| ValidatorOptInHub | `0x1059155bD5854224bF58e43CD3EEC6B07b4F30Ad` |
| ValidatorOptInRouter (legacy, three paths only) | `0x821798d7b9d57dF7Ed7616ef9111A616aB19ed64` |
| Symbiotic network | `0x9101eda106A443A0fA82375936D0D1680D5a64F5` |

## Hoodi (chain id 560048)

| Contract | Address |
| --- | --- |
| VanillaRegistry | `0x536f0792c5d5ed592e67a9260606c85f59c312f0` |
| MevCommitAVS | `0xdF8649d298ad05f019eE4AdBD6210867B8AB225F` |
| MevCommitMiddleware | `0x8E847EC4a36c8332652aB3b2B7D5c54dE29c7fde` |
| ValidatorOptInHub | `0x953c2a669493A126fd50E9f56306f254B4e49709` |
| ValidatorOptInRouter | `0xa380ba6d6083a4Cb2a3B62b0a81Ea8727861c13e` |
| Lido registry | `0xefd6333907fc73c1ac3167d843488b8899bac91b` |
| RocketMinipoolRegistry | `0xbe5a803a7b68f442eff1953c672a3499779680b0` |
| Symbiotic network / vault | `0x1623fE21185c92BB43bD83741E226288B516134a` / `0x6b23e5a4f3eb2ffc408346df0a41e9de83b4da8a` |

Source: https://docs.primev.xyz/v1.2.x/developers/networks/mainnet and testnet snippets.

## Methods
# Contract calls

`$RPC` is the operator's L1 RPC. `$KEY` is keystore / wallet flags, never a pasted key in the prompt.

BLS keys are `bytes`: `0x` + 96 hex.

## Vanilla

```bash
cast call $VANILLA "minStake()(uint256)" --rpc-url $RPC
cast send $VANILLA "stake(bytes[])" "[$KEY1,$KEY2]" --value $WEI --rpc-url $RPC
cast send $VANILLA "unstake(bytes[])" "[$KEY1]" --rpc-url $RPC
cast call $VANILLA "unstakePeriodBlocks()(uint256)" --rpc-url $RPC
cast send $VANILLA "withdraw(bytes[])" "[$KEY1]" --rpc-url $RPC
cast call $VANILLA "getStakedAmount(bytes)(uint256)" $KEY1 --rpc-url $RPC
cast call $VANILLA "isValidatorOptedIn(bytes)(bool)" $KEY1 --rpc-url $RPC
```

`msg.value` is split evenly across keys in that tx. Floor is `minStake * n`. Documented `minStake` is 0.0001 ETH; always read it on-chain. Hoodi `stake` is `onlyWhitelistedStaker` (mainnet whitelist coming).

## EigenLayer

```bash
cast send $AVS "registerValidatorsByPodOwners(bytes[][],address[])" "[[$KEY1,$KEY2]]" "[$POD_OWNER]" --rpc-url $RPC
cast send $AVS "requestValidatorsDeregistration(bytes[])" "[$KEY1]" --rpc-url $RPC
cast send $AVS "deregisterValidators(bytes[])" "[$KEY1]" --rpc-url $RPC
cast call $AVS "isValidatorOptedIn(bytes)(bool)" $KEY1 --rpc-url $RPC
cast call $AVS "getOperatorRegInfo(address)(...)" $OPERATOR --rpc-url $RPC
```

Do not use the obsolete singular `registerValidatorsByPodOwner`. Operator registration is `primev/eigen-operator-cli`, not this skill.

## Symbiotic

```bash
cast send $MIDDLEWARE "registerValidators(bytes[][],address[])" "[[$KEY1,$KEY2]]" "[$VAULT]" --rpc-url $RPC
cast send $MIDDLEWARE "requestValDeregistrations(bytes[])" "[$KEY1]" --rpc-url $RPC
cast send $MIDDLEWARE "deregisterValidators(bytes[])" "[$KEY1]" --rpc-url $RPC
cast call $MIDDLEWARE "isValidatorOptedIn(bytes)(bool)" $KEY1 --rpc-url $RPC
cast call $MIDDLEWARE "isValidatorSlashable(bytes)(bool)" $KEY1 --rpc-url $RPC
cast call $MIDDLEWARE "getNumSlashableVals(address,address)(uint256)" $VAULT $OPERATOR --rpc-url $RPC
```

Vault / operator / network setup: docs Symbiotic page and `ExampleSetup.s.sol`. This skill only registers keys after that setup exists.

## Hub (preferred verify)

```bash
cast call $HUB "isValidatorOptedIn(bytes)(bool)" $KEY1 --rpc-url $RPC
cast call $HUB "areValidatorsOptedIn(bytes[])(bool[])" "[$KEY1,$KEY2]" --rpc-url $RPC
```

## Simulate then send

```bash
cast call $CONTRACT "$SIG" $ARGS --from $SENDER --rpc-url $RPC
cast send $CONTRACT "$SIG" $ARGS --rpc-url $RPC
cast receipt $TX --rpc-url $RPC
```

If the sender is a Safe / multi-sig, do not `cast send`. Encode with `cast calldata` and follow the Safe section. Completion is still Hub `areValidatorsOptedIn`.

## Errors
# Reverts

Always report: contract, function, revert name, the address or pubkey that failed, and the missing step.

| Revert | Meaning | Tell the operator |
| --- | --- | --- |
| `VaultNotRegistered` | Vault is not on MevCommitMiddleware | Primev must `registerVaults` first. Give them the vault address. |
| `OperatorNotEntity` / `OperatorNotRegistered` | Signer is not a registered operator | Run the Symbiotic / AVS setup in docs, or ask Primev to `registerOperators` for this address. |
| `ValidatorsNotSlashable` | Not enough slashable collateral for this many keys | Increase vault allocation or register fewer keys. `getNumSlashableVals(vault, operator)` is the cap. |
| `SenderIsNotWhitelistedStaker` | Vanilla whitelist | Ask Primev to whitelist this operator address. Hoodi is live; mainnet whitelist is coming. |
| `SenderNotPodOwnerOrOperator` | Signer is neither pod owner nor delegated AVS operator | Switch to the pod-owner or operator key. |
| `ValidatorNotActiveWithEigenCore` | Pubkey is not an active EigenPod validator | Confirm the key is natively restaked and active on EigenLayer. |
| `NoPodExists` | No pods for this account | The signer has no EigenPod / delegation to use. |
| `ValidatorRecordMustNotExist` | Key already registered | Skip already-opted-in keys. Verify on the Hub first. |

If the revert is unknown, print the raw error data and still include contract + function + pubkey.

## Stake
# On-chain ETH stake

Never report `32 ETH × opted-in key count`. Sum real balances.

## Beacon effective balance (required for restaked / beacon-backed keys)

Each opted-in BLS key has an effective balance on the beacon chain. Resolve it:

1. Operator's beacon RPC: `/eth/v1/beacon/states/head/validators/0xPUBKEY` → `data.validator.effective_balance` (gwei).
2. Or beaconcha.in: `GET https://beaconcha.in/api/v1/validator/{pubkey}` with `BEACONCHAIN_API_KEY` if required.

If neither is available, ask for a beacon RPC or a beaconcha.in API key. Do not substitute 32 ETH.

## Vanilla extra stake

```bash
cast call $VANILLA "getStakedAmount(bytes)(uint256)" $KEY --rpc-url $RPC
```

This is ETH locked in VanillaRegistry, separate from beacon stake. Add it. Do not treat it as the validator's 32 ETH.

## EigenLayer restake

Read actual shares / underlying from EigenLayer core for the pod owner or operator (`StrategyManager`, `DelegationManager`, EigenPod). Do not assume 32 ETH per key. Beacon effective balance still applies to each pubkey.

## Symbiotic

Report slashable / allocated collateral from middleware + vault views (`getNumSlashableVals`, vault stake toward the operator). Token may be ETH or an ERC20. Name the asset. Do not convert unknown tokens into “32 ETH”.

## Output

For a set of keys, print a table: pubkey, Hub opted-in, beacon effective ETH, vanilla extra ETH, restake/collateral notes, row total. Then a sum. If a row cannot be resolved, say why and omit it from the sum rather than filling 32.
