ethereum.forks.amsterdam.requests

EIP-7685 generalizes how the execution layer communicates validator actions to the consensus layer. Rather than adding a dedicated header field for each new action type (as EIP-4895 did for withdrawals), the execution header commits to a single requests_hash that aggregates an ordered list of typed requests.

Each request is a type byte (see DEPOSIT_REQUEST_TYPE, WITHDRAWAL_REQUEST_TYPE, CONSOLIDATION_REQUEST_TYPE, BUILDER_DEPOSIT_REQUEST_TYPE, and BUILDER_EXIT_REQUEST_TYPE) followed by an opaque payload. Deposit requests are discovered by scanning transaction receipts for logs emitted by the deposit contract; withdrawal, consolidation, and builder deposit/exit requests (EIP-8282) are produced by the corresponding system contracts during block processing.

See parse_deposit_requests for how deposit logs become request data, compute_requests_hash for how the list is hashed for inclusion in the header, and process_general_purpose_requests for how the requests are processed.

DEPOSIT_CONTRACT_ADDRESS

Mainnet address of the beacon chain deposit contract. Scanning block receipts for logs emitted by this address is how the execution layer discovers validator deposits, per EIP-6110.

49
DEPOSIT_CONTRACT_ADDRESS = hex_to_address(
50
    "0x00000000219ab540356cbb839cbe05303d7705fa"
51
)

DEPOSIT_EVENT_SIGNATURE_HASH

First log topic of the deposit contract's DepositEvent, equal to the keccak256 of its Solidity event signature. Logs whose first topic does not match this are ignored when collecting deposit requests.

60
DEPOSIT_EVENT_SIGNATURE_HASH = hex_to_bytes32(
61
    "0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"
62
)

DEPOSIT_REQUEST_TYPE

Request type byte identifying a deposit request, per EIP-6110.

71
DEPOSIT_REQUEST_TYPE = b"\x00"

WITHDRAWAL_REQUEST_TYPE

Request type byte identifying an execution-triggered withdrawal request, per EIP-7002.

78
WITHDRAWAL_REQUEST_TYPE = b"\x01"

CONSOLIDATION_REQUEST_TYPE

Request type byte identifying a consolidation request, per EIP-7251.

86
CONSOLIDATION_REQUEST_TYPE = b"\x02"

BUILDER_DEPOSIT_REQUEST_TYPE

Request type byte identifying a builder deposit request, per EIP-8282.

93
BUILDER_DEPOSIT_REQUEST_TYPE = b"\x03"

BUILDER_EXIT_REQUEST_TYPE

Request type byte identifying a builder exit request, per EIP-8282.

100
BUILDER_EXIT_REQUEST_TYPE = b"\x04"

DEPOSIT_EVENT_LENGTH

Total length in bytes of the ABI-encoded DepositEvent data payload. Every well-formed event has this exact length.

108
DEPOSIT_EVENT_LENGTH = Uint(576)

PUBKEY_OFFSET

Position within the event payload of the validator public key's length prefix, as emitted by the Solidity ABI encoder.

114
PUBKEY_OFFSET = Uint(160)

WITHDRAWAL_CREDENTIALS_OFFSET

Position within the event payload of the withdrawal credentials' length prefix.

120
WITHDRAWAL_CREDENTIALS_OFFSET = Uint(256)

AMOUNT_OFFSET

Position within the event payload of the deposit amount's length prefix.

126
AMOUNT_OFFSET = Uint(320)

SIGNATURE_OFFSET

Position within the event payload of the deposit signature's length prefix.

131
SIGNATURE_OFFSET = Uint(384)

INDEX_OFFSET

Position within the event payload of the deposit index's length prefix.

136
INDEX_OFFSET = Uint(512)

PUBKEY_SIZE

Length of the BLS12-381 public key that identifies the validator receiving the deposit.

141
PUBKEY_SIZE = Uint(48)

WITHDRAWAL_CREDENTIALS_SIZE

Length of the withdrawal credentials, which determine where the staked ether may eventually be withdrawn.

147
WITHDRAWAL_CREDENTIALS_SIZE = Uint(32)

AMOUNT_SIZE

Length of the little-endian Gwei amount being deposited.

153
AMOUNT_SIZE = Uint(8)

SIGNATURE_SIZE

Length of the BLS12-381 signature over the deposit message.

158
SIGNATURE_SIZE = Uint(96)

INDEX_SIZE

Length of the monotonically-increasing deposit index assigned by the deposit contract when it emits the event.

163
INDEX_SIZE = Uint(8)

extract_deposit_data

Strip the Solidity ABI framing from a DepositEvent payload and return the concatenated raw fields in the order consumed by the consensus layer: public key, withdrawal credentials, amount, signature, and deposit index.

Because each field has a fixed length, every well-formed event has an identical byte layout. Any deviation indicates a misbehaving or compromised deposit contract, so this function raises InvalidBlock rather than silently accepting unexpected data.

def extract_deposit_data(data: Bytes) -> Bytes:
171
    <snip>
184
    if ulen(data) != DEPOSIT_EVENT_LENGTH:
185
        raise InvalidBlock("Invalid deposit event data length")
186
187
    # Check that all the offsets are in order
188
    pubkey_offset = Uint.from_be_bytes(data[0:32])
189
    if pubkey_offset != PUBKEY_OFFSET:
190
        raise InvalidBlock("Invalid pubkey offset in deposit log")
191
192
    withdrawal_credentials_offset = Uint.from_be_bytes(data[32:64])
193
    if withdrawal_credentials_offset != WITHDRAWAL_CREDENTIALS_OFFSET:
194
        raise InvalidBlock(
195
            "Invalid withdrawal credentials offset in deposit log"
196
        )
197
198
    amount_offset = Uint.from_be_bytes(data[64:96])
199
    if amount_offset != AMOUNT_OFFSET:
200
        raise InvalidBlock("Invalid amount offset in deposit log")
201
202
    signature_offset = Uint.from_be_bytes(data[96:128])
203
    if signature_offset != SIGNATURE_OFFSET:
204
        raise InvalidBlock("Invalid signature offset in deposit log")
205
206
    index_offset = Uint.from_be_bytes(data[128:160])
207
    if index_offset != INDEX_OFFSET:
208
        raise InvalidBlock("Invalid index offset in deposit log")
209
210
    # Check that all the sizes are in order
211
    pubkey_size = Uint.from_be_bytes(
212
        data[pubkey_offset : pubkey_offset + Uint(32)]
213
    )
214
    if pubkey_size != PUBKEY_SIZE:
215
        raise InvalidBlock("Invalid pubkey size in deposit log")
216
217
    pubkey = data[
218
        pubkey_offset + Uint(32) : pubkey_offset + Uint(32) + PUBKEY_SIZE
219
    ]
220
221
    withdrawal_credentials_size = Uint.from_be_bytes(
222
        data[
223
            withdrawal_credentials_offset : withdrawal_credentials_offset
224
            + Uint(32)
225
        ],
226
    )
227
    if withdrawal_credentials_size != WITHDRAWAL_CREDENTIALS_SIZE:
228
        raise InvalidBlock(
229
            "Invalid withdrawal credentials size in deposit log"
230
        )
231
232
    withdrawal_credentials = data[
233
        withdrawal_credentials_offset
234
        + Uint(32) : withdrawal_credentials_offset
235
        + Uint(32)
236
        + WITHDRAWAL_CREDENTIALS_SIZE
237
    ]
238
239
    amount_size = Uint.from_be_bytes(
240
        data[amount_offset : amount_offset + Uint(32)]
241
    )
242
    if amount_size != AMOUNT_SIZE:
243
        raise InvalidBlock("Invalid amount size in deposit log")
244
245
    amount = data[
246
        amount_offset + Uint(32) : amount_offset + Uint(32) + AMOUNT_SIZE
247
    ]
248
249
    signature_size = Uint.from_be_bytes(
250
        data[signature_offset : signature_offset + Uint(32)]
251
    )
252
    if signature_size != SIGNATURE_SIZE:
253
        raise InvalidBlock("Invalid signature size in deposit log")
254
255
    signature = data[
256
        signature_offset + Uint(32) : signature_offset
257
        + Uint(32)
258
        + SIGNATURE_SIZE
259
    ]
260
261
    index_size = Uint.from_be_bytes(
262
        data[index_offset : index_offset + Uint(32)]
263
    )
264
    if index_size != INDEX_SIZE:
265
        raise InvalidBlock("Invalid index size in deposit log")
266
267
    index = data[
268
        index_offset + Uint(32) : index_offset + Uint(32) + INDEX_SIZE
269
    ]
270
271
    return pubkey + withdrawal_credentials + amount + signature + index

parse_deposit_requests

Walk the receipts produced during block execution, concatenating the raw payload of every valid deposit event into a single byte string.

A log is considered a deposit when it originates from DEPOSIT_CONTRACT_ADDRESS and its first topic matches DEPOSIT_EVENT_SIGNATURE_HASH. The returned bytes are the direct concatenation of the unframed deposit fields, ready to be prefixed with DEPOSIT_REQUEST_TYPE before being appended to the block's request list.

def parse_deposit_requests(block_output: BlockOutput) -> Bytes:
275
    <snip>
290
    deposit_requests: Bytes = b""
291
    for key in block_output.receipt_keys:
292
        receipt = trie_get(block_output.receipts_trie, key)
293
        assert receipt is not None
294
        decoded_receipt = decode_receipt(receipt)
295
        for log in decoded_receipt.logs:
296
            if log.address == DEPOSIT_CONTRACT_ADDRESS:
297
                if (
298
                    len(log.topics) > 0
299
                    and log.topics[0] == DEPOSIT_EVENT_SIGNATURE_HASH
300
                ):
301
                    request = extract_deposit_data(log.data)
302
                    deposit_requests += request
303
304
    return deposit_requests

compute_requests_hash

Compute the SHA2-256 commitment over an ordered list of type-prefixed requests, as defined by EIP-7685.

The commitment is the SHA2-256 hash of the concatenation of the SHA2-256 hashes of each individual request. This is what the execution header's requests_hash stores, and what the consensus layer re-derives to validate that both layers observed the same set of requests.

def compute_requests_hash(requests: List[Bytes]) -> Bytes:
308
    <snip>
322
    m = sha256()
323
    for request in requests:
324
        m.update(sha256(request).digest())
325
326
    return m.digest()