# Teku: EIP-7732 ePBS state transition and fork transition (Gloas), checked against the spec

Item `teku`, batch `cl-state`. This review reads public code and specs only. Nothing was run against any network or node.

## How this was read (limits)

- The local shell could not run in this sandbox. Every command failed with `apply-seccomp: write /proc/self/setgroups ... Permission denied`, so no shallow clone was possible.
- All code was read over HTTPS from `raw.githubusercontent.com` at pinned SHAs, and from `api.github.com` (commits, trees, pulls, search). No GitHub HTML pages were read.
- **Teku line numbers** come from a fetch tool that counts lines in the raw file. They are not from `grep -n`.
  - When the same file (`BlockProcessorGloas.java`) was fetched twice, the numbers differed by up to 2 lines.
  - Treat each Teku `#Lx-Ly` as ±3 lines. The quoted code text is exact.
- **Spec citations** name the function and the raw URL at the pin. Spec line numbers could not be counted reliably with the tools available, so none are given.
- Verbatim quotes are short (≤125 characters each), because the fetch tool limits quote length.

## Pins

| Repo | Ref | Commit | Source |
|---|---|---|---|
| Consensys-Incorporated/teku | master | `2c35574b3373cbe8ad121a768d98f066aec05b73` (2026-10-27T16:51:17Z, "DataColumnSidecarArchiveReconstructor improvements + tests + wiring (#11508)") | https://api.github.com/repos/Consensys-Incorporated/teku/commits/master |
| ethereum/consensus-specs | master | `8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2` (2026-10-27T16:28:12Z, "Fix block access list hash in EL block hash test helper (#5773)") | https://api.github.com/repos/ethereum/consensus-specs/commits/master |
| ethereum/consensus-specs | v1.7.0-beta.4 | tag (used only for the fork.md comparison) | https://raw.githubusercontent.com/ethereum/consensus-specs/v1.7.0-beta.4/specs/gloas/fork.md |
| sigp/lighthouse | stable | `a376defa30470923556d91bdeb15a802e297f31f` (2026-10-22, "Release v8.3.2 (#10433)") | https://api.github.com/repos/sigp/lighthouse/commits/stable |

Notes on the pins:
- `T` below means `https://raw.githubusercontent.com/Consensys-Incorporated/teku/2c35574b3373cbe8ad121a768d98f066aec05b73/`.
- Teku's Gloas logic lives under `ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/gloas/`, shortened below to `G/`.
- `S` means `https://raw.githubusercontent.com/ethereum/consensus-specs/8a80ffa82bd49aac0dd6db17f9e8150aa257fbc2/specs/gloas/`.

**Which spec version Teku targets.** Teku's fork onboarding code follows **v1.7.0-beta.4**, not master.
- It matches the beta.4 `onboard_builders_from_pending_deposits` branch for branch (see C1).
- Teku's open PR #11582 says that "the current v1.7.0-beta.4 vectors encode the old routing", and that five `gloas/fork/fork` reference tests will fail until test vectors include consensus-specs #5736.
- Spec PR #5736, "Separate builder and validator deposits at the Gloas fork", was merged on 2026-10-26T19:16:28Z as `8b374fa81b0918b84fe0063a56b4a482e1fa2c6f`.

EIP status at the time of reading:
- EIP-7732 is in Last Call, deadline 2026-11-01.
- EIP-8282 (Builder Execution Requests, requires EIP-7732) is in Last Call.
- Neither is live on mainnet, so the details withholding for live code does not apply to the Gloas-only code below.

## Candidates

### C1 — spec-version lag: builder onboarding at the Gloas fork follows beta.4, not master (PR 5736)

**(1) Spec (master), `S` + `fork.md`, `onboard_builders_from_pending_deposits`.** Verbatim lines, in order:
- `# Only builder-credential deposits can fund builders at the fork.`
- `if not is_builder_withdrawal_credential(deposit.withdrawal_credentials):` → `pending_deposits.append(deposit)` → `continue`
- `builder_pubkeys = [builder.pubkey for builder in state.builders]`
- `if deposit.pubkey not in builder_pubkeys:`
  - `if not is_valid_deposit_signature(` … `):`
    - `# Preserve validator top-ups when no builder can receive them.`
    - `if deposit.pubkey in validator_pubkeys or is_pending_validator(` … `pending_deposits.append(deposit)`
    - `continue`
  - `add_builder_to_registry(` … `PAYLOAD_BUILDER_VERSION,` …
- `else:` → `state.builders[builder_index].balance += deposit.amount`

Beta.4 (https://raw.githubusercontent.com/ethereum/consensus-specs/v1.7.0-beta.4/specs/gloas/fork.md) checks in a different order:
1. `# Deposits for existing validators stay in the pending queue` / `if deposit.pubkey in validator_pubkeys:` → keep, `continue`.
2. Then, under `if deposit.pubkey not in builder_pubkeys:`:
   - `if not is_builder_withdrawal_credential(...)` → keep
   - `if is_pending_validator(pending_deposits, deposit.pubkey):` → keep
   - `if not is_valid_deposit_signature(...)` → `continue` (drop)
   - otherwise `add_builder_to_registry`
3. `else:` → top up the builder.

PR #5736 (https://api.github.com/repos/ethereum/consensus-specs/pulls/5736) summarises the change:
- "Validator deposits stay in the validator queue, even if a builder uses the same key."
- A valid builder deposit registers or tops up a builder even if a validator uses the same key.

**(2) Teku**, `T` + `G/forktransition/GloasStateUpgrade.java`. `upgrade` calls `onboardBuildersFromPendingDeposits(state)` at #L205. The method body is #L209-L318:
- #L222: the loop walks `state.getPendingDeposits()` in queue order.
- #L224-L228: if `getValidatorIndex` finds the pubkey, the deposit is kept and the loop continues. This happens before any credential check, which is the beta.4 order.
- #L229-L237: if `getBuilderIndex` finds the pubkey, the builder's balance is increased by the amount and the deposit is consumed. The deposit's credentials are not checked on this path.
- #L246-L249: `predicates.isBuilderWithdrawalCredential(...)` false → kept, and recorded in `PendingValidatorTracker`.
- #L251-L253: if the pubkey is a pending validator → kept.
- #L255-L268:
  - `isValidDepositSignature` false → dropped (#L259-L260).
  - Otherwise `addBuilderToRegistry(... PAYLOAD_BUILDER_VERSION ...)` is called.
- #L272-L273: the surviving deposits are written back.

**(3) When results differ.** Each case requires the fork-epoch state's `pending_deposits` to contain the deposits described. The resulting post-fork state root differs in every case:
- **(a)** A pending deposit has the 0xB0 builder credential and a **valid** signature, and its pubkey already belongs to a validator.
  - Master: a builder is registered with that pubkey and the deposit is consumed.
  - Teku: the deposit stays in `pending_deposits` and no builder is created.
- **(b)** A pending deposit has a non-0xB0 credential (0x00/0x01/0x02), and its pubkey belongs to a builder created earlier in the same onboarding pass by an earlier 0xB0 deposit.
  - Master: the deposit stays in the validator queue.
  - Teku: the builder's balance is credited with the deposit and the deposit is removed.
- **(c)** A pending deposit has the 0xB0 credential and a valid signature, and an earlier deposit in the queue with a non-builder credential and a valid signature already made the pubkey a pending validator.
  - Master: a builder is registered.
  - Teku: the deposit stays queued.

Cases that agree with master:
- An invalid-signature 0xB0 deposit for a known or pending validator: kept in both.
- An invalid-signature 0xB0 deposit for an unknown pubkey: dropped in both.
- A 0xB0 deposit for an existing builder: credited in both, with no signature check in either.

**(4) Lighthouse stable** `a376defa`, `consensus/state_processing/src/upgrade/gloas.rs#L235-L318`. Lighthouse **agrees with Teku (beta.4)**:
- #L260 `if state.get_validator_index(&deposit.pubkey)?.is_some() {` and #L261 `pending_deposits.push(deposit.clone());` come first.
- The builder top-up is at #L297-L302.
- `!is_builder_withdrawal_credential` is at #L268, inside the not-a-builder arm.
- The pending-validator check is #L278; the signature checks are #L270 and #L283.

The brief says Prysm also follows beta.4; Prysm was not read for this item.

**(5) Confidence: high** that Teku's code differs from master. **Medium** on practical reach: it needs a deposit of the shapes above in the pending queue at the fork epoch.

Disproof attempts:
- (i) I fetched beta.4 and master `fork.md` and compared them line by line. Teku matches beta.4 in branch order and outcomes.
- (ii) I checked whether Teku reorders the checks elsewhere. `upgrade` calls only this method (#L205).
- (iii) I checked whether master's change might be test-only. It changes the executable spec, and Teku's PR #11582 says the fork reference tests change with it.

The difference stands at the pinned commit.

**(6) Severity: consensus split** at the Gloas fork epoch, between clients on master (spec) and clients on beta.4 (Teku, Lighthouse, Prysm). Under the brief's taxonomy it is a **spec-version lag**. It disappears once #11582 or an equivalent fix is merged and clients converge.

**Known-check:** **known**.
- https://github.com/Consensys-Incorporated/teku/pull/11582: "Route pending deposits by credentials at Gloas builder onboarding". **Open** at the time of reading; created 2026-10-27, head `40935962690daedd83ac5ed53a96557d15641153`. It implements consensus-specs #5736.
- Tracking issue https://github.com/Consensys-Incorporated/teku/issues/11510 ("look into consensus specs -builder deposits at upgrade", links PR 5736): **closed** 2026-10-19.

### No other candidates

None of the other rules in scope produced a difference between Teku and the spec at the pin; they are listed below. Results that rest on code I could not read are marked "partial".

## Rules checked and found consistent with the spec

Spec text is from `S` + `beacon-chain.md` unless another file is named. Teku paths are under `T`. Lines are ±3, as explained above.

| # | Rule (spec function) | Spec says (short) | Teku file#lines (behaviour) | Verdict |
|---|---|---|---|---|
| 1 | `process_execution_payload_bid`: self-build | `assert amount == 0`; `assert signed_bid.signature == bls.G2_POINT_AT_INFINITY` | `G/block/BlockProcessorGloas.java#L245-L252`: `builderIndex.equals(BUILDER_INDEX_SELF_BUILD)`, `!amount.isZero()`, `!...isInfinity()` | match |
| 2 | bid: active builder, version, cover, signature | `is_active_builder`; `version == PAYLOAD_BUILDER_VERSION`; `can_builder_cover_bid`; `verify_execution_payload_bid_signature` (else branch only) | `BlockProcessorGloas.java#L254-L270` (same order, else branch only) | match |
| 3 | bid: blob count | `len(bid.blob_kzg_commitments) <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block` | `BlockProcessorGloas.java#L275-L281` (blob parameters for the current epoch, `>` → reject) | match |
| 4 | bid: slot / parent hash / block hash ≠ parent / parent root / prev_randao | `bid.slot == state.slot`; `state.slot > GENESIS_SLOT`; `parent_block_hash == state.latest_block_hash`; `block_hash != parent_block_hash`; `parent_block_root == get_block_root_at_slot(state, state.slot - 1)`; `prev_randao == get_randao_mix(state, get_current_epoch(state))` | `BlockProcessorGloas.java#L284-L306`. Teku uses `state.getSlot().minusMinZero(1)` and has no explicit `slot > GENESIS_SLOT` check. That is equivalent, because a block is never processed at slot 0 (`process_block_header` needs `block.slot > latest_block_header.slot`). | match (cosmetic only) |
| 5 | pending payment index and fields | `builder_pending_payments[SLOTS_PER_EPOCH + bid.slot % SLOTS_PER_EPOCH]`; `weight=0`, withdrawal(fee_recipient, amount, builder_index), `proposer_index=get_beacon_proposer_index` | `BlockProcessorGloas.java#L307-L329`: `bid.getSlot().mod(SPE).plus(SPE)`, weight `UInt64.ZERO`, `create(bid.getFeeRecipient(), amount, builderIndex)`, `getBeaconProposerIndex(state)`; only when `amount > 0`; bid cached | match |
| 6 | `can_builder_cover_bid` boundaries | `if builder_balance < min_balance: return False`; `return builder_balance - min_balance >= bid_amount`; `min_balance = MIN_DEPOSIT_AMOUNT + pending` | `G/helpers/BeaconStateAccessorsGloas.java#L161-L172`: `builderBalance.isLessThan(minBalance)`; `minusMinZero(minBalance).isGreaterThanOrEqualTo(bidAmount)` | match (equality accepted in both) |
| 7 | `get_pending_balance_to_withdraw_for_builder` | sum over `builder_pending_withdrawals` and `builder_pending_payments` | `BeaconStateAccessorsGloas.java#L144-L159` (both lists) | match |
| 8 | `is_active_builder` | `deposit_epoch < finalized_checkpoint.epoch and withdrawable_epoch == FAR_FUTURE_EPOCH` | `G/helpers/PredicatesGloas.java#L50-L60`. An out-of-range index returns false, where the spec would raise an IndexError; the block is rejected either way. | match |
| 9 | `process_parent_execution_payload`: empty parent | `if bid.parent_block_hash != parent_bid.block_hash:` → `assert requests == ExecutionRequests.empty()` → return | `BlockProcessorGloas.java#L134-L151`: `!isBidBuildingOnFullParent` (`MiscHelpersGloas.java#L268-L271`: `bid.getParentBlockHash().equals(state.getLatestExecutionPayloadBid().getBlockHash())`), `!requests.isDefault()` → reject | match |
| 10 | full parent: requests root | `hash_tree_root(requests) == parent_bid.execution_requests_root` | `BlockProcessorGloas.java#L154-L157`, `MiscHelpersGloas.java#L302-L307` | match |
| 11 | `apply_parent_execution_payload`: payment settle index | current epoch `SLOTS_PER_EPOCH + parent_slot % SPE`; previous epoch `parent_slot % SPE`; older and `value > 0` → append withdrawal | `BlockProcessorGloas.java#L168-L190` (parentSlot from `getLatestBlockHeader().getSlot()`, same three branches) | match |
| 12 | apply: request order | deposits, withdrawals, consolidations, builder_deposits, builder_exits | `BlockProcessorGloas.java#L197-L210` (same order) | match |
| 13 | apply: availability bit and latest_block_hash | set the parent slot bit; `latest_block_hash = parent_bid.block_hash` | `BlockProcessorGloas.java#L214-L218` (`parentSlot.mod(SLOTS_PER_HISTORICAL_ROOT)`) | match |
| 14 | apply: per-type request limits | asserts `len(...) <= MAX_*_PER_PAYLOAD` for withdrawals, consolidations, builder deposits, builder exits; none for deposits | No asserts in apply. Limits sit on the SSZ types: `ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/registry/SchemaRegistryBuilder.java` ~#L475 (deposits: `SszProgressiveListSchema.create(...)` with no max), ~#L496-L498, #L519-L521, #L732-L734, #L749-L751 (progressive lists with `getMax*RequestsPerPayload()`) | **partial**: equivalent only if Teku's progressive-list max is enforced when decoding. Not verified (the SSZ library code was not read). |
| 15 | Gloas `process_withdrawals`: early return | `if state.latest_block_hash != state.latest_execution_payload_bid.block_hash: return` | `G/withdrawals/WithdrawalsHelpersGloas.java#L58-L62` (`!isParentBlockFull`); `MiscHelpersGloas.java#L278-L280`: `getLatestExecutionPayloadBid().getBlockHash().equals(getLatestBlockHash())` | match |
| 16 | withdrawals: order and limits | builder pending withdrawals, then partials, then builders sweep, then validators sweep; builder limits `MAX_WITHDRAWALS_PER_PAYLOAD - 1`; `min(len(builders), MAX_BUILDERS_PER_WITHDRAWALS_SWEEP)`; sweep condition `withdrawable_epoch <= epoch and balance > 0`; `next = (idx + processed) % len(builders)` | `WithdrawalsHelpersGloas.java#L67-L131` (limits at #L72, #L112, #L116-L117; condition at #L126-L127; flag conversion at #L87, #L131); pending-withdrawals sublist at #L196-L197; next builder index at #L204-L208 (guarded for an empty list) | match. **Partial** for the partial-withdrawals and validator-sweep steps, which are inherited from `WithdrawalsHelpersElectra` (shared with live code, not reviewed). |
| 17 | `process_payload_attestation` | `data.beacon_block_root == state.latest_block_header.parent_root`; `data.slot + 1 == state.slot`; `is_valid_indexed_payload_attestation` | `BlockProcessorGloas.java#L426-L455` | match |
| 18 | `get_indexed_payload_attestation` | indices from PTC by bits, `sorted`, duplicates kept | `BeaconStateAccessorsGloas.java#L179-L195` (ascending sort, no dedup) | match |
| 19 | `is_valid_indexed_payload_attestation` | empty or not sorted → False (non-strict, duplicates allowed); `get_domain(state, DOMAIN_PTC_ATTESTER, compute_epoch_at_slot(attestation.data.slot))`; `FastAggregateVerify` | `G/util/AttestationUtilGloas.java#L83-L104` (`indices.isEmpty() \|\| !Comparators.isInOrder(...)`, `Domain.PTC_ATTESTER`, epoch from `data.getSlot()`) | match |
| 20 | `compute_ptc` | seed `sha256(get_seed(state, epoch, DOMAIN_PTC_ATTESTER) + uint_to_bytes(slot))`; all slot committees concatenated; `size=PTC_SIZE, shuffle_indices=False` | `BeaconStateAccessorsGloas.java#L202-L227` | match |
| 21 | `compute_balance_weighted_selection` (16-bit) | `offset = i % 16 * 2`; 2-byte random value; `MAX_RANDOM_VALUE = 2**16 - 1`; `effective_balance * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_ELECTRA * random_value` | `G/helpers/MiscHelpersGloas.java#L157-L190` (`(i % 16) * 2`, `slice(offset, 2)`, `isGreaterThanOrEqualTo`, `nextIndex = i % total`, shuffled only if the flag is set) | match (the value of the `MAX_RANDOM_VALUE` constant was not read) |
| 22 | `get_ptc` window indexing | previous epoch `ptc_window[slot % SPE]` (requires `epoch + 1 == state_epoch`); current/future `(epoch - state_epoch + 1) * SPE + slot % SPE`, `epoch <= state_epoch + MIN_SEED_LOOKAHEAD`, `epoch >= GLOAS_FORK_EPOCH` | `BeaconStateAccessorsGloas.java#L235-L267` | match |
| 23 | `initialize_ptc_window` (fork.md) | SPE zeroed PTCs, then `compute_ptc` for epochs `current .. current + MIN_SEED_LOOKAHEAD` | `BeaconStateAccessorsGloas.java#L275-L302` | match |
| 24 | `process_ptc_window` | shift by SPE; fill `get_current_epoch + MIN_SEED_LOOKAHEAD + 1` | `G/statetransition/epoch/EpochProcessorGloas.java#L113-L135` | match |
| 25 | `get_builder_payment_quorum_threshold` | `total_active // SPE * 6 // 10` | `BeaconStateAccessorsGloas.java#L309-L313` (same integer order) | match |
| 26 | `process_builder_pending_payments` | first SPE entries, `weight >= quorum` → append; move the second half to the front; reset the second half | `EpochProcessorGloas.java#L83-L106` | match. Where this runs in the epoch order (after `process_pending_consolidations`, before effective-balance updates) was **not verified**: the epoch sequencer is in a parent class that was not read. |
| 27 | `process_attestation` builder weight | `will_set_new_flag and had_no_participation and is_attestation_same_slot and amount > 0`; index by target epoch | `BlockProcessorGloas.java#L344-L373` (index and conditions). The new-flag gate is in the caller `versions/altair/block/BlockProcessorAltair.java#L201-L222` (`if (newParticipationFlags != 0)`); that caller is shared Altair code, but this hook does nothing before Gloas (#L247-L256) | match |
| 28 | `data.index < 2` | `assert data.index < 2` | `G/operations/validation/AttestationDataValidatorGloas.java#L41-L46` | match |
| 29 | envelope (`fork-choice.md`, `verify_execution_payload_envelope`) | signature; `beacon_block_root == hash_tree_root(header)`; `parent_beacon_block_root == latest_block_header.parent_root`; builder_index, prev_randao, gas_limit, block_hash, requests root vs bid; `payload.slot_number == state.slot`; parent_hash == `latest_block_hash`; timestamp; withdrawals root == `payload_expected_withdrawals`; versioned hashes from `bid.blob_kzg_commitments`; `verify_and_notify_new_payload` | `G/execution/ExecutionPayloadVerifierGloas.java#L94-L180`: all checks present, at #L104-L168; the slot check at #L119 uses `envelope.getSlot()`, which returns the payload's `getSlotNumber()` (`.../epbs/versions/gloas/ExecutionPayloadEnvelope.java#L80-L82`); parent root at #L116; the engine call at #L170-L177 | match. The check order differs, which does not change the verdict. |
| 30 | `upgrade_to_gloas` defaults | `latest_block_hash = header.block_hash`; empty builders; `next_withdrawal_builder_index = 0`; availability all `0b1`; empty payments and withdrawals; bid = {parent_hash, parent_root=`latest_block_header.parent_root`, block_hash, prev_randao, fee_recipient=0, gas_limit, `BUILDER_INDEX_SELF_BUILD`, slot=`latest_block_header.slot`, 0, 0, [], `htr(ExecutionRequests.empty())`}; empty `payload_expected_withdrawals`; `ptc_window = initialize_ptc_window`; then onboarding | `G/forktransition/GloasStateUpgrade.java#L128-L205` (every field as in the spec; availability uses `ofBits(range(0, SLOTS_PER_HISTORICAL_ROOT))`) | match (all-ones bitvector assumed from the `ofBits` call; the schema was not read) |
| 31 | SSZ: `BeaconState` | ProgressiveContainer with 46 fields; `latest_block_hash` replaces `latest_execution_payload_header` at position 25; then builders … ptc_window at 39-46 | `.../state/beaconstate/versions/gloas/BeaconStateSchemaGloas.java#L73-L74` (`allActive` 46), #L106-L163 (header index replaced by `LATEST_BLOCK_HASH`; new fields at indices 38-45 in spec order) | match |
| 32 | SSZ: `ExecutionPayloadBid` (12 fields, progressive) | parent_block_hash, parent_block_root, block_hash, prev_randao, fee_recipient, gas_limit, builder_index, slot, value, execution_payment, blob_kzg_commitments, execution_requests_root | `.../epbs/versions/gloas/ExecutionPayloadBidSchema.java#L65`, #L71-L82 | match |
| 33 | SSZ: `ExecutionPayloadEnvelope` (5 fields, progressive) | payload, execution_requests, builder_index, beacon_block_root, parent_beacon_block_root | `.../epbs/versions/gloas/ExecutionPayloadEnvelopeSchema.java#L41`, #L48-L55 | match |
| 34 | SSZ: `ExecutionRequests` (5 fields, progressive) | deposits, withdrawals, consolidations, builder_deposits, builder_exits | `.../execution/versions/gloas/ExecutionRequestsSchemaGloas.java#L42-L49`, #L68-L84 | match |
| 35 | SSZ: `Builder` | pubkey, version:uint8, execution_address, balance, deposit_epoch, withdrawable_epoch | `.../state/versions/gloas/Builder.java#L43-L48` | match |
| 36 | SSZ: other containers | `BuilderPendingPayment`, `BuilderPendingWithdrawal`, `PayloadAttestationData`, `PayloadAttestation`, `IndexedPayloadAttestation`, `PayloadAttestationMessage`, `BeaconBlockBody` (13 fields, progressive) | schema files located in the tree but **not read** | not verified |
| 37 | constants | `BUILDER_INDEX_SELF_BUILD = UINT64_MAX`; `BUILDER_INDEX_FLAG = 2**40`; prefix `0xB0`; numerator/denominator 6/10; domains 0x0B000000 (BEACON_BUILDER), 0x0C000000 (PTC_ATTESTER), 0x0D000000 (PROPOSER_PREFERENCES), 0x0E000000 (BUILDER_DEPOSIT); `PTC_SIZE = 512`; `MAX_PAYLOAD_ATTESTATIONS = 4`; `MAX_BUILDER_DEPOSIT/EXIT_REQUESTS_PER_PAYLOAD = 64/16`; `MAX_BUILDERS_PER_WITHDRAWALS_SWEEP = 16384` | `ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/SpecConfigGloas.java#L22-L27`; `.../spec/constants/Domain.java#L36-L39`; `.../spec/constants/WithdrawalPrefixes.java#L26-L27` (`(byte) 0xB0`); `ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/mainnet/gloas.yaml` L6, L11, L19, L21, L33 | match |
| 38 | builder-index flag conversion | `convert_builder_index_to_validator_index` sets the 2**40 flag | `MiscHelpersGloas.java#L97-L103` (`\| BUILDER_INDEX_FLAG`, `& ~BUILDER_INDEX_FLAG`); `PredicatesGloas.java#L45-L47` | match |
| 39 | `get_index_for_new_builder` | reuse an index where `withdrawable_epoch <= current_epoch and balance == 0`, else `len(builders)` | `BeaconStateAccessorsGloas.java#L372-L382` (described by the fetch tool as "first builder that is withdrawable with zero balance") | likely match (exact comparison not quoted) |

**Shared with live (Fulu) code.** These were not reviewed, and nothing about them is reported beyond this note:
- the Electra/Fulu withdrawal sweeps that Gloas inherits;
- `BlockProcessorAltair.processAttestation`, apart from the Gloas hook.

## Known-check

| Topic | Result | Queries / URLs |
|---|---|---|
| C1 onboarding vs PR 5736 | **known**: PR #11582 open; issue #11510 closed | `api.github.com/search/issues?q=repo:Consensys-Incorporated/teku+onboard` (12 hits); `...+5736` (5 hits); `...+gloas+builder+deposits` (18 hits, only #11582 open); https://api.github.com/repos/Consensys-Incorporated/teku/pulls/11582 ; https://api.github.com/repos/Consensys-Incorporated/teku/issues/11510 |
| Onboarding O(K²) | already indexed: #11154 issue closed 2026-08-24; fix PR #11155 merged 2026-08-24; related #11511 merged 2026-10-18, #10729 merged 2026-05-29 | same searches |
| Payment order | already indexed by the brief: #11408 merged (not re-fetched) | — |
| 10682 | already indexed by the brief as closed (not re-fetched) | — |

No other candidate needed a known-check. Hive and execution-spec-tests results were not consulted for this item, because no candidate besides C1 came up and C1 is already confirmed by Teku's own PR.

## URLs read

Pins:
- https://api.github.com/repos/Consensys-Incorporated/teku/commits/master
- https://api.github.com/repos/ethereum/consensus-specs/commits/master
- https://api.github.com/repos/sigp/lighthouse/commits/stable

Spec:
- `S`beacon-chain.md
- `S`fork.md
- `S`fork-choice.md
- https://raw.githubusercontent.com/ethereum/consensus-specs/v1.7.0-beta.4/specs/gloas/fork.md
- https://api.github.com/repos/ethereum/consensus-specs/pulls/5736
- `builder.md` was not read separately. Its envelope rules are covered by `fork-choice.md` `verify_execution_payload_envelope`.

EIPs:
- https://eips.ethereum.org/EIPS/eip-7732
- https://eips.ethereum.org/EIPS/eip-8282

Teku, all under `T`:
- tree API: https://api.github.com/repos/Consensys-Incorporated/teku/git/trees/2c35574b3373cbe8ad121a768d98f066aec05b73:ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/gloas?recursive=1 and the same for `.../spec/datastructures`
- `G/block/BlockProcessorGloas.java`
- `G/helpers/BeaconStateAccessorsGloas.java`
- `G/helpers/MiscHelpersGloas.java`
- `G/helpers/PredicatesGloas.java`
- `G/util/AttestationUtilGloas.java`
- `G/withdrawals/WithdrawalsHelpersGloas.java`
- `G/statetransition/epoch/EpochProcessorGloas.java`
- `G/forktransition/GloasStateUpgrade.java`
- `G/execution/ExecutionPayloadVerifierGloas.java`
- `G/operations/validation/AttestationDataValidatorGloas.java`
- `ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/block/BlockProcessorAltair.java`
- `.../spec/datastructures/execution/versions/gloas/ExecutionRequestsSchemaGloas.java`
- `.../spec/datastructures/execution/versions/gloas/ExecutionRequestsFieldsGloas.java`
- `.../spec/schemas/registry/SchemaRegistryBuilder.java`
- `.../spec/datastructures/state/beaconstate/versions/gloas/BeaconStateSchemaGloas.java`
- `.../spec/datastructures/epbs/versions/gloas/ExecutionPayloadBidSchema.java`
- `.../spec/datastructures/epbs/versions/gloas/ExecutionPayloadEnvelopeSchema.java`
- `.../spec/datastructures/epbs/versions/gloas/ExecutionPayloadEnvelope.java`
- `.../spec/datastructures/state/versions/gloas/Builder.java`
- `.../spec/config/SpecConfigGloas.java`
- `.../spec/constants/Domain.java`
- `.../spec/constants/WithdrawalPrefixes.java`
- `.../resources/tech/pegasys/teku/spec/config/presets/mainnet/gloas.yaml`

Lighthouse:
- https://raw.githubusercontent.com/sigp/lighthouse/a376defa30470923556d91bdeb15a802e297f31f/consensus/state_processing/src/upgrade/gloas.rs

Teku search: the three `api.github.com/search/issues` queries listed above.
