@lodestar/api 1.42.0-rc.0 → 1.43.0-dev.07452fe3b7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -226,6 +226,18 @@ export type Endpoints = {
226
226
  EmptyMeta
227
227
  >;
228
228
 
229
+ /**
230
+ * Get signed execution payload envelope.
231
+ * Retrieves signed execution payload envelope for a given block id.
232
+ */
233
+ getSignedExecutionPayloadEnvelope: Endpoint<
234
+ "GET",
235
+ BlockArgs,
236
+ {params: {block_id: string}},
237
+ gloas.SignedExecutionPayloadEnvelope,
238
+ ExecutionOptimisticFinalizedAndVersionMeta
239
+ >;
240
+
229
241
  /**
230
242
  * Get block BlobSidecar
231
243
  * Retrieves BlobSidecar included in requested block.
@@ -594,7 +606,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
594
606
  method: "POST",
595
607
  req: {
596
608
  writeReqJson: ({signedExecutionPayloadEnvelope}) => {
597
- const fork = config.getForkName(signedExecutionPayloadEnvelope.message.slot);
609
+ const fork = config.getForkName(signedExecutionPayloadEnvelope.message.payload.slotNumber);
598
610
  return {
599
611
  body: getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.toJson(signedExecutionPayloadEnvelope),
600
612
  headers: {
@@ -609,7 +621,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
609
621
  };
610
622
  },
611
623
  writeReqSsz: ({signedExecutionPayloadEnvelope}) => {
612
- const fork = config.getForkName(signedExecutionPayloadEnvelope.message.slot);
624
+ const fork = config.getForkName(signedExecutionPayloadEnvelope.message.payload.slotNumber);
613
625
  return {
614
626
  body: getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.serialize(signedExecutionPayloadEnvelope),
615
627
  headers: {
@@ -634,6 +646,15 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
634
646
  requestWireFormat: WireFormat.ssz,
635
647
  },
636
648
  },
649
+ getSignedExecutionPayloadEnvelope: {
650
+ url: "/eth/v1/beacon/execution_payload_envelope/{block_id}",
651
+ method: "GET",
652
+ req: blockIdOnlyReq,
653
+ resp: {
654
+ data: WithVersion((fork) => getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope),
655
+ meta: ExecutionOptimisticFinalizedAndVersionCodec,
656
+ },
657
+ },
637
658
  getBlobSidecars: {
638
659
  url: "/eth/v1/beacon/blob_sidecars/{block_id}",
639
660
  method: "GET",
@@ -1,6 +1,13 @@
1
1
  import {ValueOf} from "@chainsafe/ssz";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
- import {ForkPostElectra, ForkPreElectra, isForkPostElectra} from "@lodestar/params";
3
+ import {
4
+ ForkName,
5
+ ForkPostElectra,
6
+ ForkPreElectra,
7
+ MAX_PAYLOAD_ATTESTATIONS,
8
+ PTC_SIZE,
9
+ isForkPostElectra,
10
+ } from "@lodestar/params";
4
11
  import {
5
12
  ArrayOf,
6
13
  AttesterSlashing,
@@ -37,6 +44,8 @@ const ProposerSlashingListType = ArrayOf(ssz.phase0.ProposerSlashing);
37
44
  const SignedVoluntaryExitListType = ArrayOf(ssz.phase0.SignedVoluntaryExit);
38
45
  const SignedBLSToExecutionChangeListType = ArrayOf(ssz.capella.SignedBLSToExecutionChange);
39
46
  const SyncCommitteeMessageListType = ArrayOf(ssz.altair.SyncCommitteeMessage);
47
+ const PayloadAttestationListType = ArrayOf(ssz.gloas.PayloadAttestation, MAX_PAYLOAD_ATTESTATIONS);
48
+ const PayloadAttestationMessageListType = ArrayOf(ssz.gloas.PayloadAttestationMessage, PTC_SIZE);
40
49
 
41
50
  type AttestationListPhase0 = ValueOf<typeof AttestationListTypePhase0>;
42
51
  type AttestationListElectra = ValueOf<typeof AttestationListTypeElectra>;
@@ -50,6 +59,8 @@ type ProposerSlashingList = ValueOf<typeof ProposerSlashingListType>;
50
59
  type SignedVoluntaryExitList = ValueOf<typeof SignedVoluntaryExitListType>;
51
60
  type SignedBLSToExecutionChangeList = ValueOf<typeof SignedBLSToExecutionChangeListType>;
52
61
  type SyncCommitteeMessageList = ValueOf<typeof SyncCommitteeMessageListType>;
62
+ type PayloadAttestationList = ValueOf<typeof PayloadAttestationListType>;
63
+ type PayloadAttestationMessageList = ValueOf<typeof PayloadAttestationMessageListType>;
53
64
 
54
65
  export type Endpoints = {
55
66
  /**
@@ -76,6 +87,18 @@ export type Endpoints = {
76
87
  VersionMeta
77
88
  >;
78
89
 
90
+ /**
91
+ * Get payload attestations from operations pool
92
+ * Retrieves payload attestations known by the node but not necessarily incorporated into any block.
93
+ */
94
+ getPoolPayloadAttestations: Endpoint<
95
+ "GET",
96
+ {slot?: Slot},
97
+ {query: {slot?: number}},
98
+ PayloadAttestationList,
99
+ VersionMeta
100
+ >;
101
+
79
102
  /**
80
103
  * Get AttesterSlashings from operations pool
81
104
  * Retrieves attester slashings known by the node but not necessarily incorporated into any block
@@ -244,6 +267,18 @@ export type Endpoints = {
244
267
  EmptyResponseData,
245
268
  EmptyMeta
246
269
  >;
270
+
271
+ /**
272
+ * Submit payload attestation messages
273
+ * Submits payload attestation messages to the beacon node.
274
+ */
275
+ submitPayloadAttestationMessages: Endpoint<
276
+ "POST",
277
+ {payloadAttestationMessages: PayloadAttestationMessageList},
278
+ {body: unknown; headers: {[MetaHeader.Version]: string}},
279
+ EmptyResponseData,
280
+ EmptyMeta
281
+ >;
247
282
  };
248
283
 
249
284
  export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoints> {
@@ -274,6 +309,19 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
274
309
  meta: VersionCodec,
275
310
  },
276
311
  },
312
+ getPoolPayloadAttestations: {
313
+ url: "/eth/v1/beacon/pool/payload_attestations",
314
+ method: "GET",
315
+ req: {
316
+ writeReq: ({slot}) => ({query: {slot}}),
317
+ parseReq: ({query}) => ({slot: query.slot}),
318
+ schema: {query: {slot: Schema.Uint}},
319
+ },
320
+ resp: {
321
+ data: PayloadAttestationListType,
322
+ meta: VersionCodec,
323
+ },
324
+ },
277
325
  getPoolAttesterSlashings: {
278
326
  url: "/eth/v1/beacon/pool/attester_slashings",
279
327
  method: "GET",
@@ -499,5 +547,32 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
499
547
  },
500
548
  resp: EmptyResponseCodec,
501
549
  },
550
+ submitPayloadAttestationMessages: {
551
+ url: "/eth/v1/beacon/pool/payload_attestations",
552
+ method: "POST",
553
+ req: {
554
+ writeReqJson: ({payloadAttestationMessages}) => ({
555
+ body: PayloadAttestationMessageListType.toJson(payloadAttestationMessages),
556
+ headers: {[MetaHeader.Version]: ForkName.gloas},
557
+ }),
558
+ parseReqJson: ({body, headers}) => {
559
+ toForkName(fromHeaders(headers, MetaHeader.Version));
560
+ return {payloadAttestationMessages: PayloadAttestationMessageListType.fromJson(body)};
561
+ },
562
+ writeReqSsz: ({payloadAttestationMessages}) => ({
563
+ body: PayloadAttestationMessageListType.serialize(payloadAttestationMessages),
564
+ headers: {[MetaHeader.Version]: ForkName.gloas},
565
+ }),
566
+ parseReqSsz: ({body, headers}) => {
567
+ toForkName(fromHeaders(headers, MetaHeader.Version));
568
+ return {payloadAttestationMessages: PayloadAttestationMessageListType.deserialize(body)};
569
+ },
570
+ schema: {
571
+ body: Schema.ObjectArray,
572
+ headers: {[MetaHeader.Version]: Schema.String},
573
+ },
574
+ },
575
+ resp: EmptyResponseCodec,
576
+ },
502
577
  };
503
578
  }
@@ -16,12 +16,13 @@ import {
16
16
  altair,
17
17
  capella,
18
18
  electra,
19
+ gloas,
19
20
  phase0,
20
21
  ssz,
21
22
  sszTypesFor,
22
23
  } from "@lodestar/types";
23
24
  import {EmptyMeta, EmptyResponseCodec, EmptyResponseData} from "../../utils/codecs.js";
24
- import {getPostAltairForkTypes, getPostBellatrixForkTypes} from "../../utils/fork.js";
25
+ import {getPostAltairForkTypes, getPostBellatrixForkTypes, getPostGloasForkTypes} from "../../utils/fork.js";
25
26
  import {Endpoint, RouteDefinitions, Schema} from "../../utils/index.js";
26
27
  import {VersionType} from "../../utils/metadata.js";
27
28
 
@@ -99,12 +100,14 @@ export enum EventType {
99
100
  blobSidecar = "blob_sidecar",
100
101
  /** The node has received a valid DataColumnSidecar (from P2P or API) */
101
102
  dataColumnSidecar = "data_column_sidecar",
102
- /** The node has received an execution payload (from P2P or API) that is successfully imported on the fork-choice `on_execution_payload` handler */
103
+ /** The node has received a `SignedExecutionPayloadEnvelope` (from P2P or API) that is successfully imported on the fork-choice `on_execution_payload` handler */
103
104
  executionPayload = "execution_payload",
104
- /** The node has received an execution payload (from P2P or API) that passes validation rules of the `execution_payload` topic */
105
+ /** The node has received a `SignedExecutionPayloadEnvelope` (from P2P or API) that passes validation rules of the `execution_payload` topic */
105
106
  executionPayloadGossip = "execution_payload_gossip",
106
107
  /** The node has verified that the execution payload and blobs for a block are available and ready for payload attestation */
107
108
  executionPayloadAvailable = "execution_payload_available",
109
+ /** The node has received a `SignedExecutionPayloadBid` (from P2P or API) that passes gossip validation on the `execution_payload_bid` topic */
110
+ executionPayloadBid = "execution_payload_bid",
108
111
  }
109
112
 
110
113
  export const eventTypes: {[K in EventType]: K} = {
@@ -128,6 +131,7 @@ export const eventTypes: {[K in EventType]: K} = {
128
131
  [EventType.executionPayload]: EventType.executionPayload,
129
132
  [EventType.executionPayloadGossip]: EventType.executionPayloadGossip,
130
133
  [EventType.executionPayloadAvailable]: EventType.executionPayloadAvailable,
134
+ [EventType.executionPayloadBid]: EventType.executionPayloadBid,
131
135
  };
132
136
 
133
137
  export type EventData = {
@@ -182,7 +186,6 @@ export type EventData = {
182
186
  builderIndex: BuilderIndex;
183
187
  blockHash: RootHex;
184
188
  blockRoot: RootHex;
185
- stateRoot: RootHex;
186
189
  executionOptimistic: boolean;
187
190
  };
188
191
  [EventType.executionPayloadGossip]: {
@@ -190,12 +193,12 @@ export type EventData = {
190
193
  builderIndex: BuilderIndex;
191
194
  blockHash: RootHex;
192
195
  blockRoot: RootHex;
193
- stateRoot: RootHex;
194
196
  };
195
197
  [EventType.executionPayloadAvailable]: {
196
198
  slot: Slot;
197
199
  blockRoot: RootHex;
198
200
  };
201
+ [EventType.executionPayloadBid]: {version: ForkName; data: gloas.SignedExecutionPayloadBid};
199
202
  };
200
203
 
201
204
  export type BeaconEvent = {[K in EventType]: {type: K; message: EventData[K]}}[EventType];
@@ -371,7 +374,6 @@ export function getTypeByEvent(config: ChainForkConfig): {[K in EventType]: Type
371
374
  builderIndex: ssz.BuilderIndex,
372
375
  blockHash: stringType,
373
376
  blockRoot: stringType,
374
- stateRoot: stringType,
375
377
  executionOptimistic: ssz.Boolean,
376
378
  },
377
379
  {jsonCase: "eth2"}
@@ -382,7 +384,6 @@ export function getTypeByEvent(config: ChainForkConfig): {[K in EventType]: Type
382
384
  builderIndex: ssz.BuilderIndex,
383
385
  blockHash: stringType,
384
386
  blockRoot: stringType,
385
- stateRoot: stringType,
386
387
  },
387
388
  {jsonCase: "eth2"}
388
389
  ),
@@ -393,6 +394,7 @@ export function getTypeByEvent(config: ChainForkConfig): {[K in EventType]: Type
393
394
  },
394
395
  {jsonCase: "eth2"}
395
396
  ),
397
+ [EventType.executionPayloadBid]: WithVersion((fork) => getPostGloasForkTypes(fork).SignedExecutionPayloadBid),
396
398
 
397
399
  [EventType.lightClientOptimisticUpdate]: WithVersion(
398
400
  (fork) => getPostAltairForkTypes(fork).LightClientOptimisticUpdate
@@ -1,6 +1,17 @@
1
1
  import {ContainerType, Type, ValueOf} from "@chainsafe/ssz";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
- import {ArrayOf, BeaconState, Epoch, RootHex, Slot, ValidatorIndex, ssz} from "@lodestar/types";
3
+ import {ForkName} from "@lodestar/params";
4
+ import {
5
+ ArrayOf,
6
+ AttesterSlashing,
7
+ BeaconState,
8
+ Epoch,
9
+ RootHex,
10
+ SignedBeaconBlock,
11
+ Slot,
12
+ ValidatorIndex,
13
+ ssz,
14
+ } from "@lodestar/types";
4
15
  import {
5
16
  EmptyArgs,
6
17
  EmptyMeta,
@@ -11,10 +22,13 @@ import {
11
22
  JsonOnlyResponseCodec,
12
23
  WithVersion,
13
24
  } from "../../utils/codecs.js";
25
+ import {toForkName} from "../../utils/fork.js";
26
+ import {fromHeaders} from "../../utils/headers.js";
14
27
  import {Endpoint, RouteDefinitions, Schema} from "../../utils/index.js";
15
28
  import {
16
29
  ExecutionOptimisticFinalizedAndVersionCodec,
17
30
  ExecutionOptimisticFinalizedAndVersionMeta,
31
+ MetaHeader,
18
32
  VersionCodec,
19
33
  VersionMeta,
20
34
  } from "../../utils/metadata.js";
@@ -382,9 +396,27 @@ export type Endpoints = {
382
396
  CustodyInfo,
383
397
  EmptyMeta
384
398
  >;
399
+
400
+ /** Craft attester slashings from the attestations in the provided blocks */
401
+ getAttesterSlashingsFromBlocks: Endpoint<
402
+ "POST",
403
+ {signedBlocks: SignedBeaconBlock[]},
404
+ {body: unknown; headers: {[MetaHeader.Version]: string}},
405
+ AttesterSlashing[],
406
+ VersionMeta
407
+ >;
385
408
  };
386
409
 
387
- export function getDefinitions(_config: ChainForkConfig): RouteDefinitions<Endpoints> {
410
+ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoints> {
411
+ function assertBlocksMatchFork(signedBlocks: SignedBeaconBlock[], expectedFork: ForkName): void {
412
+ for (const block of signedBlocks) {
413
+ const blockFork = config.getForkName(block.message.slot);
414
+ if (blockFork !== expectedFork) {
415
+ throw new Error(`Block at slot ${block.message.slot} is from fork ${blockFork}, expected ${expectedFork}`);
416
+ }
417
+ }
418
+ }
419
+
388
420
  return {
389
421
  writeHeapdump: {
390
422
  url: "/eth/v1/lodestar/write_heapdump",
@@ -602,5 +634,47 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions<Endpo
602
634
  req: EmptyRequestCodec,
603
635
  resp: JsonOnlyResponseCodec,
604
636
  },
637
+ getAttesterSlashingsFromBlocks: {
638
+ url: "/eth/v1/lodestar/blocks/attester_slashings",
639
+ method: "POST",
640
+ req: {
641
+ writeReqJson: ({signedBlocks}) => {
642
+ if (signedBlocks.length === 0) throw new Error("No blocks provided.");
643
+ const fork = config.getForkName(signedBlocks[0].message.slot);
644
+ return {
645
+ body: ArrayOf(ssz[fork].SignedBeaconBlock).toJson(signedBlocks as SignedBeaconBlock<typeof fork>[]),
646
+ headers: {[MetaHeader.Version]: fork},
647
+ };
648
+ },
649
+ parseReqJson: ({body, headers}) => {
650
+ const fork = toForkName(fromHeaders(headers, MetaHeader.Version));
651
+ const signedBlocks = ArrayOf(ssz[fork].SignedBeaconBlock).fromJson(body) as SignedBeaconBlock[];
652
+ assertBlocksMatchFork(signedBlocks, fork);
653
+ return {signedBlocks};
654
+ },
655
+ writeReqSsz: ({signedBlocks}) => {
656
+ if (signedBlocks.length === 0) throw new Error("No blocks provided.");
657
+ const fork = config.getForkName(signedBlocks[0].message.slot);
658
+ return {
659
+ body: ArrayOf(ssz[fork].SignedBeaconBlock).serialize(signedBlocks as SignedBeaconBlock<typeof fork>[]),
660
+ headers: {[MetaHeader.Version]: fork},
661
+ };
662
+ },
663
+ parseReqSsz: ({body, headers}) => {
664
+ const fork = toForkName(fromHeaders(headers, MetaHeader.Version));
665
+ const signedBlocks = ArrayOf(ssz[fork].SignedBeaconBlock).deserialize(body) as SignedBeaconBlock[];
666
+ assertBlocksMatchFork(signedBlocks, fork);
667
+ return {signedBlocks};
668
+ },
669
+ schema: {
670
+ body: Schema.ObjectArray,
671
+ headers: {[MetaHeader.Version]: Schema.String},
672
+ },
673
+ },
674
+ resp: {
675
+ data: WithVersion((fork) => ArrayOf(ssz[fork].AttesterSlashing)),
676
+ meta: VersionCodec,
677
+ },
678
+ },
605
679
  };
606
680
  }
@@ -131,6 +131,18 @@ export const ProposerDutyType = new ContainerType(
131
131
  {jsonCase: "eth2"}
132
132
  );
133
133
 
134
+ export const PtcDutyType = new ContainerType(
135
+ {
136
+ /** Public key of the validator in the registry */
137
+ pubkey: ssz.BLSPubkey,
138
+ /** Index of validator in the registry. */
139
+ validatorIndex: ssz.ValidatorIndex,
140
+ /** The slot at which the validator must perform PTC duties. */
141
+ slot: ssz.Slot,
142
+ },
143
+ {jsonCase: "eth2"}
144
+ );
145
+
134
146
  /**
135
147
  * From https://github.com/ethereum/beacon-APIs/pull/134
136
148
  */
@@ -219,6 +231,7 @@ export const LivenessResponseDataType = new ContainerType(
219
231
  export const ValidatorIndicesType = ArrayOf(ssz.ValidatorIndex);
220
232
  export const AttesterDutyListType = ArrayOf(AttesterDutyType);
221
233
  export const ProposerDutyListType = ArrayOf(ProposerDutyType);
234
+ export const PtcDutyListType = ArrayOf(PtcDutyType);
222
235
  export const SyncDutyListType = ArrayOf(SyncDutyType);
223
236
  export const SignedAggregateAndProofListPhase0Type = ArrayOf(ssz.phase0.SignedAggregateAndProof);
224
237
  export const SignedAggregateAndProofListElectraType = ArrayOf(ssz.electra.SignedAggregateAndProof);
@@ -239,6 +252,8 @@ export type AttesterDuty = ValueOf<typeof AttesterDutyType>;
239
252
  export type AttesterDutyList = ValueOf<typeof AttesterDutyListType>;
240
253
  export type ProposerDuty = ValueOf<typeof ProposerDutyType>;
241
254
  export type ProposerDutyList = ValueOf<typeof ProposerDutyListType>;
255
+ export type PtcDuty = ValueOf<typeof PtcDutyType>;
256
+ export type PtcDutyList = ValueOf<typeof PtcDutyListType>;
242
257
  export type SyncDuty = ValueOf<typeof SyncDutyType>;
243
258
  export type SyncDutyList = ValueOf<typeof SyncDutyListType>;
244
259
  export type SignedAggregateAndProofListPhase0 = ValueOf<typeof SignedAggregateAndProofListPhase0Type>;
@@ -336,6 +351,23 @@ export type Endpoints = {
336
351
  ExecutionOptimisticMeta
337
352
  >;
338
353
 
354
+ /**
355
+ * Get PTC duties
356
+ * Requests the beacon node to provide a set of Payload Timeliness Committee duties for a particular epoch.
357
+ */
358
+ getPtcDuties: Endpoint<
359
+ "POST",
360
+ {
361
+ /** Must not be greater than the next epoch */
362
+ epoch: Epoch;
363
+ /** An array of the validator indices for which to obtain the duties */
364
+ indices: ValidatorIndices;
365
+ },
366
+ {params: {epoch: Epoch}; body: unknown},
367
+ PtcDutyList,
368
+ ExecutionOptimisticAndDependentRootMeta
369
+ >;
370
+
339
371
  /**
340
372
  * Requests a beacon node to produce a valid block, which can then be signed by a validator.
341
373
  * Metadata in the response indicates the type of block produced, and the supported types of block
@@ -440,6 +472,21 @@ export type Endpoints = {
440
472
  EmptyMeta
441
473
  >;
442
474
 
475
+ /**
476
+ * Produce payload attestation data
477
+ * Requests that the beacon node produce a PayloadAttestationData.
478
+ */
479
+ producePayloadAttestationData: Endpoint<
480
+ "GET",
481
+ {
482
+ /** The slot for which payload attestation data should be created */
483
+ slot: Slot;
484
+ },
485
+ {params: {slot: Slot}},
486
+ gloas.PayloadAttestationData,
487
+ VersionMeta
488
+ >;
489
+
443
490
  produceSyncCommitteeContribution: Endpoint<
444
491
  "GET",
445
492
  {
@@ -698,6 +745,24 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
698
745
  meta: ExecutionOptimisticCodec,
699
746
  },
700
747
  },
748
+ getPtcDuties: {
749
+ url: "/eth/v1/validator/duties/ptc/{epoch}",
750
+ method: "POST",
751
+ req: {
752
+ writeReqJson: ({epoch, indices}) => ({params: {epoch}, body: ValidatorIndicesType.toJson(indices)}),
753
+ parseReqJson: ({params, body}) => ({epoch: params.epoch, indices: ValidatorIndicesType.fromJson(body)}),
754
+ writeReqSsz: ({epoch, indices}) => ({params: {epoch}, body: ValidatorIndicesType.serialize(indices)}),
755
+ parseReqSsz: ({params, body}) => ({epoch: params.epoch, indices: ValidatorIndicesType.deserialize(body)}),
756
+ schema: {
757
+ params: {epoch: Schema.UintRequired},
758
+ body: Schema.StringArray,
759
+ },
760
+ },
761
+ resp: {
762
+ data: PtcDutyListType,
763
+ meta: ExecutionOptimisticAndDependentRootCodec,
764
+ },
765
+ },
701
766
  produceBlockV3: {
702
767
  url: "/eth/v3/validator/blocks/{slot}",
703
768
  method: "GET",
@@ -780,7 +845,8 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
780
845
  (data as BlockContents).block as BeaconBlock<ForkPreDeneb> // <- tranformation
781
846
  );
782
847
  },
783
- deserialize(data, {executionPayloadBlinded, version}) {
848
+ deserialize(data, meta) {
849
+ const {executionPayloadBlinded, version} = meta as ProduceBlockV3Meta;
784
850
  return executionPayloadBlinded
785
851
  ? getPostBellatrixForkTypes(version).BlindedBeaconBlock.deserialize(data)
786
852
  : isForkPostDeneb(version)
@@ -934,6 +1000,21 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
934
1000
  meta: EmptyMetaCodec,
935
1001
  },
936
1002
  },
1003
+ producePayloadAttestationData: {
1004
+ url: "/eth/v1/validator/payload_attestation_data/{slot}",
1005
+ method: "GET",
1006
+ req: {
1007
+ writeReq: ({slot}) => ({params: {slot}}),
1008
+ parseReq: ({params}) => ({slot: params.slot}),
1009
+ schema: {
1010
+ params: {slot: Schema.UintRequired},
1011
+ },
1012
+ },
1013
+ resp: {
1014
+ data: ssz.gloas.PayloadAttestationData,
1015
+ meta: VersionCodec,
1016
+ },
1017
+ },
937
1018
  produceSyncCommitteeContribution: {
938
1019
  url: "/eth/v1/validator/sync_committee_contribution",
939
1020
  method: "GET",
@@ -118,7 +118,10 @@ export type ResponseDataCodec<T, M> = {
118
118
  toJson: (data: T, meta: M) => unknown; // server
119
119
  fromJson: (data: unknown, meta: M) => T; // client
120
120
  serialize: (data: T, meta: M) => Uint8Array; // server
121
- deserialize: (data: Uint8Array, meta: M) => T; // client
121
+ // `meta` is typed as `any` so SSZ `Type<T>.deserialize(data, opts?)` satisfies the shape.
122
+ // Route-specific deserializers (e.g. `WithVersion`) cast to their expected meta internally.
123
+ // biome-ignore lint/suspicious/noExplicitAny: contravariant workaround for ssz Type.deserialize(opts?)
124
+ deserialize: (data: Uint8Array, meta: any) => T; // client
122
125
  };
123
126
 
124
127
  export type ResponseMetadataCodec<T> = {