Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions playbooks/gloas-dev/deploy-eip8282-contracts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
id: deploy-eip8282-contracts
name: "GLOAS: Deploy EIP-8282 builder request contracts (pre-fork)"
description: |
Deploys the EIP-8282 builder deposit (request type 0x03) and builder exit
(request type 0x04) system contracts onto a running devnet BEFORE the GLOAS
fork activates.

glamsterdam-devnet-7 deliberately omits these predeploys from genesis
(DEPLOY_EIP8282_CONTRACTS=false in the genesis generator) to exercise the
mainnet-like deployment workflow: from GLOAS activation onward every block
is INVALID unless both contracts exist, so they must be deployed as regular
transactions pre-fork. Intended to be registered as a startup test so the
contracts land right after the devnet comes up.

Deployment goes through the deterministic CREATE2 factory (predeployed in
devnet genesis as a well-known contract) using the vanity-mined salts from
ethereum/execution-specs#3091 — CREATE2(factory, salt, initcode) yields the
canonical ...8282 addresses the clients expect. The factory REVERTS when
CREATE2 fails (including the already-deployed collision case), so a
successful receipt is proof of deployment.

Idempotent: an eth_call precheck against the builder deposit contract's fee
getter detects existing code and skips deployment, so restarts of a
startup-scheduled run stay green. The exit contract cannot be prechecked the
same way — its constructor arms the slot-0 excess inhibitor, which makes its
fee getter revert by design until the first post-GLOAS system call — so its
deployment is asserted via the deploy receipt (both contracts only ever
deploy as a pair).
version: 1.0.0
tags: [gloas, builder, eip-8282, deployment, prefork]
timeout: 30m
schedule:
startup: true
config:
walletPrivkey: ''
# Deterministic CREATE2 deployment factory (predeployed in devnet genesis).
create2Factory: '0x4e59b44847b379578588920cA78FbF26c0B4956C'
# EIP-8282 predeploy addresses (canonical, from the EIP / execution-specs#3091).
builderDepositContract: '0x0000bFF46984e3725691FA540a8C7589300D8282'
builderExitContract: '0x000064D678505ad48F8cCb093BC65613800E8282'
# Factory calldata: 32-byte vanity-mined salt ++ initcode, verbatim from
# ethereum/execution-specs#3091 builder_deposit_factory_deploy.json.
# The initcode's runtime is byte-identical to the egg v6.1.3 genesis
# predeploy (source: ethereum/sys-asm src/builder_deposits).
depositDeployData: '0x1f4f2c41c28e816e259b621c58b94b37309c8dec42c8f6e400001a46c9d96bf761027480600a5f395ff33373fffffffffffffffffffffffffffffffffffffffe1461011c575f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146102705760015460088111605257506058565b60089003015b601190600182026001905f5b5f821115607f57810190830284830290049160010191906064565b90939004925050503660b814609f57366102705734610270575f5260205ff35b8034106102705760383567ffffffffffffffff1680633b9aca001161027057633b9aca00029034031061027057600154600101600155600354806006026004015f358155600101602035815560010160403581556001016060358155600101608035815560010160a035905560b85f5f3760b85fa0600101600355005b60035460025480820380604011610131575060405b5f5b8181146101d7578281016006026004018160b8028154815260200181600101548152602001816002015480825260401c67ffffffffffffffff16816010018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c816001015353602001816003015481526020018160040154815260200190600501549052600101610133565b91018092146101e957906002556101f4565b90505f6002555f6003555b36610242575f54600154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461023057600882820111610238575b50505f610264565b0160089003610264565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b5f555f60015560b8025ff35b5f5ffd'
# Same, from builder_exit_factory_deploy.json (source: ethereum/sys-asm
# src/builder_exits). The constructor prefix (PUSH32 2^256-1, PUSH0, SSTORE)
# arms the slot-0 excess inhibitor at deployment.
exitDeployData: '0x89abb1878437213f971f849327423ed1d9c5cdb03970cd8f0000318b3ff101197fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5f556101ca80602d5f395ff33373fffffffffffffffffffffffffffffffffffffffe1460e1575f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101c65760015460028111605157506057565b60029003015b601190600182026001905f5b5f821115607e57810190830284830290049160010191906063565b909390049250505036603014609e57366101c657346101c6575f5260205ff35b34106101c657600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260305f60143760445fa0600101600355005b6003546002548082038060101160f5575060105b5f5b81811461012d5782810160030260040181604402815460601b8152601401816001015481526020019060020154905260010160f7565b910180921461013f579060025561014a565b90505f6002555f6003555b36610198575f54600154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101865760028282011161018e575b50505f6101ba565b01600290036101ba565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b5f555f6001556044025ff35b5f5ffd'

tasks:
- name: check_clients_are_healthy
title: "Check if at least one client is ready"
timeout: 5m
config:
minClientCount: 1

# The deposit contract has no inhibitor: with code present, an empty-calldata
# eth_call hits the fee getter and returns a 32-byte fee; without code it
# returns 0x. (Never reverts, unlike the exit contract's armed fee getter.)
- name: check_eth_call
id: predeploy_check
title: "Probe builder deposit contract for existing code"
timeout: 2m
configVars:
callAddress: builderDepositContract

- name: get_consensus_specs
id: get_specs
title: "Get consensus chain specs"

- name: check_consensus_slot_range
id: current_slot
title: "Get current slot"

# Deploying after GLOAS activation is futile: without the contracts every
# block since activation is invalid, so the deploy transactions can never be
# included. Fail fast with a clear message instead of a receipt timeout.
- name: run_shell
id: fork_guard
title: "Assert GLOAS has not activated yet"
if: "tasks.predeploy_check.outputs.callResult == \"0x\""
config:
envVars:
GLOAS_FORK_EPOCH: "tasks.get_specs.outputs.specs.GLOAS_FORK_EPOCH"
SLOTS_PER_EPOCH: "tasks.get_specs.outputs.specs.SLOTS_PER_EPOCH"
CURRENT_SLOT: "tasks.current_slot.outputs.currentSlot"
command: |
set -e
GLOAS_FORK_EPOCH=$(echo "$GLOAS_FORK_EPOCH" | jq -r .)
SLOTS_PER_EPOCH=$(echo "$SLOTS_PER_EPOCH" | jq -r .)
CURRENT_SLOT=$(echo "$CURRENT_SLOT" | jq -r .)

ACTIVATION_SLOT=$((GLOAS_FORK_EPOCH * SLOTS_PER_EPOCH))
if [ "$CURRENT_SLOT" -ge "$ACTIVATION_SLOT" ]; then
echo "ERROR: GLOAS is active (slot $CURRENT_SLOT >= $ACTIVATION_SLOT)"
echo "and the EIP-8282 contracts are NOT deployed. Every block since"
echo "activation is invalid without them; the deploy transactions can"
echo "no longer be included. The network must be relaunched (or the"
echo "contracts baked into genesis via DEPLOY_EIP8282_CONTRACTS=true)."
exit 1
fi
echo "GLOAS activates at slot $ACTIVATION_SLOT (epoch $GLOAS_FORK_EPOCH), current slot $CURRENT_SLOT — proceeding."

- name: generate_child_wallet
id: deployer_wallet
title: "Generate funded wallet for the deployment transactions"
if: "tasks.predeploy_check.outputs.callResult == \"0x\""
config:
walletSeed: eip8282-deployer
prefundMinBalance: 1000000000000000000 # 1 ETH (two ~0.4M gas txs)
configVars:
privateKey: walletPrivkey

- name: generate_transaction
id: deploy_deposit_contract
title: "Deploy builder deposit contract (request type 0x03)"
if: "tasks.predeploy_check.outputs.callResult == \"0x\""
config:
gasLimit: 2000000
awaitReceipt: true
failOnReject: true
configVars:
privateKey: tasks.deployer_wallet.outputs.childWallet.privkey
targetAddress: create2Factory
callData: depositDeployData

- name: generate_transaction
id: deploy_exit_contract
title: "Deploy builder exit contract (request type 0x04)"
if: "tasks.predeploy_check.outputs.callResult == \"0x\""
config:
gasLimit: 2000000
awaitReceipt: true
failOnReject: true
configVars:
privateKey: tasks.deployer_wallet.outputs.childWallet.privkey
targetAddress: create2Factory
callData: exitDeployData

# Re-probe the deposit contract: it must now answer the fee getter. The exit
# contract intentionally reverts on eth_call while the inhibitor is armed, so
# its deployment is proven by the deploy_exit_contract receipt above.
- name: check_eth_call
id: postdeploy_check
title: "Verify builder deposit contract answers its fee getter"
timeout: 2m
configVars:
callAddress: builderDepositContract

- name: run_shell
id: verify_summary
title: "Assert deployment state and summarize"
config:
envVars:
PRE: "tasks.predeploy_check.outputs.callResult"
POST: "tasks.postdeploy_check.outputs.callResult"
DEPOSIT_ADDR: "builderDepositContract"
EXIT_ADDR: "builderExitContract"
command: |
set -e
PRE=$(echo "$PRE" | jq -r .)
POST=$(echo "$POST" | jq -r .)
DEPOSIT_ADDR=$(echo "$DEPOSIT_ADDR" | jq -r .)
EXIT_ADDR=$(echo "$EXIT_ADDR" | jq -r .)

if [ "$POST" = "0x" ]; then
echo "ERROR: builder deposit contract at $DEPOSIT_ADDR still has no code."
exit 1
fi

if [ "$PRE" = "0x" ]; then
echo "Deployed EIP-8282 contracts via CREATE2 factory:"
else
echo "EIP-8282 contracts were already deployed:"
fi
echo " builder deposit: $DEPOSIT_ADDR (fee getter returned $POST)"
echo " builder exit: $EXIT_ADDR (fee getter reverts until the first post-GLOAS system call clears the inhibitor — deployment proven by receipt)"