diff --git a/packages/api/src/beacon/routes/beacon/state.ts b/packages/api/src/beacon/routes/beacon/state.ts index 2ebe6cd6881c..2660ec87a1d0 100644 --- a/packages/api/src/beacon/routes/beacon/state.ts +++ b/packages/api/src/beacon/routes/beacon/state.ts @@ -1,7 +1,18 @@ import {ContainerType, ValueOf} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; import {MAX_VALIDATORS_PER_COMMITTEE} from "@lodestar/params"; -import {CommitteeIndex, Epoch, RootHex, Slot, StringType, ValidatorStatus, electra, phase0, ssz} from "@lodestar/types"; +import { + CommitteeIndex, + Epoch, + RootHex, + Slot, + StringType, + ValidatorStatus, + electra, + fulu, + phase0, + ssz, +} from "@lodestar/types"; import {ArrayOf, JsonOnlyReq} from "../../../utils/codecs.js"; import {Endpoint, RequestCodec, RouteDefinitions, Schema} from "../../../utils/index.js"; import { @@ -308,6 +319,19 @@ export type Endpoints = { electra.PendingConsolidations, ExecutionOptimisticFinalizedAndVersionMeta >; + + /** + * Get State Proposer Lookahead + * + * Returns proposer lookahead for state with given 'stateId'. + */ + getProposerLookahead: Endpoint< + "GET", + StateArgs, + {params: {state_id: string}}, + fulu.ProposerLookahead, + ExecutionOptimisticFinalizedAndVersionMeta + >; }; // biome-ignore lint/suspicious/noExplicitAny: @@ -552,5 +576,14 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions = { meta: {executionOptimistic: true, finalized: false, version: ForkName.electra}, }, }, + getProposerLookahead: { + args: {stateId: "head"}, + res: { + data: ssz.fulu.ProposerLookahead.defaultValue(), + meta: {executionOptimistic: true, finalized: false, version: ForkName.fulu}, + }, + }, // rewards diff --git a/packages/beacon-node/src/api/impl/beacon/state/index.ts b/packages/beacon-node/src/api/impl/beacon/state/index.ts index acfd033c5ebd..3929cad477b6 100644 --- a/packages/beacon-node/src/api/impl/beacon/state/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/state/index.ts @@ -1,9 +1,10 @@ import {routes} from "@lodestar/api"; import {ApplicationMethods} from "@lodestar/api/server"; -import {EPOCHS_PER_HISTORICAL_VECTOR, isForkPostElectra} from "@lodestar/params"; +import {EPOCHS_PER_HISTORICAL_VECTOR, isForkPostElectra, isForkPostFulu} from "@lodestar/params"; import { BeaconStateAllForks, BeaconStateElectra, + BeaconStateFulu, CachedBeaconStateAltair, computeEpochAtSlot, computeStartSlotAtEpoch, @@ -366,5 +367,21 @@ export function getBeaconStateApi({ meta: {executionOptimistic, finalized, version: fork}, }; }, + + async getProposerLookahead({stateId}, context) { + const {state, executionOptimistic, finalized} = await getState(stateId); + const fork = config.getForkName(state.slot); + + if (!isForkPostFulu(fork)) { + throw new ApiError(400, `Cannot retrieve proposer lookahead for pre-fulu state fork=${fork}`); + } + + const {proposerLookahead} = state as BeaconStateFulu; + + return { + data: context?.returnBytes ? proposerLookahead.serialize() : proposerLookahead.toValue(), + meta: {executionOptimistic, finalized, version: fork}, + }; + }, }; }