lifecycle

package
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2026 License: GPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package lifecycle provides builder lifecycle management (deposit, balance, exit) as an ePBS sub-concern.

Index

Constants

This section is empty.

Variables

View Source
var (
	// BuilderDepositContractAddress is the EIP-8282 builder deposit predeploy.
	BuilderDepositContractAddress = common.HexToAddress("0x0000bFF46984e3725691FA540a8C7589300D8282")
	// BuilderExitContractAddress is the EIP-8282 builder exit predeploy.
	BuilderExitContractAddress = common.HexToAddress("0x000064D678505ad48F8cCb093BC65613800E8282")
)

Builder system-contract addresses (EIP-8282 predeploys, injected by the EL at the Amsterdam fork). Hard-coded per the current EIP-8282 draft (ethereum-genesis-generator#300); ReadQueueFee verifies code exists at the address before any request is submitted, so a stale address fails loudly instead of sending value transfers to an empty account.

View Source
var ErrBuilderExited = errors.New("builder has exited and cannot be reactivated; deposits are disabled until the pubkey leaves the builder registry")

ErrBuilderExited is returned when a deposit/top-up targets a builder whose exit has been initiated. Exited builders can never be reactivated: the deposit would only top up the exited registry entry and be withdrawn back to the wallet by the sweep. The pubkey becomes depositable again only once it leaves the builder registry (its index is reused by a different builder's deposit).

View Source
var ErrContractNotActive = errors.New("builder deposit contract not active yet")

ErrContractNotActive is returned while the builder deposit contract still holds the pre-fork excess inhibitor (before GLOAS_FORK_EPOCH), so deposits can't be submitted yet.

View Source
var ErrContractNotDeployed = errors.New("builder system contract not deployed")

ErrContractNotDeployed is returned when a builder system contract has no code at its expected address. Before the Amsterdam fork this is normal (the EL injects the predeploys at the fork); after the fork it means the network uses different addresses than this build expects.

View Source
var ErrDepositFeeTooHigh = errors.New("builder deposit queue fee exceeds configured limit")

ErrDepositFeeTooHigh is returned when the builder deposit contract's current queue fee exceeds the operator's configured limit (DepositMaxFeeGwei). It is a signal to delay the deposit/top-up and retry later, not a hard failure.

View Source
var ErrNoDepositContract = errors.New("no validator deposit contract address in spec")

ErrNoDepositContract is returned when the beacon spec does not advertise a regular validator deposit contract address, so early onboarding cannot submit a deposit.

Functions

func BuildBuilderDepositCalldata

func BuildBuilderDepositCalldata(
	pubkey []byte,
	withdrawalCredentials []byte,
	amountGwei uint64,
	signature []byte,
) ([]byte, error)

BuildBuilderDepositCalldata builds the 184-byte builder deposit request calldata: pubkey(48) ++ withdrawal_credentials(32) ++ amount(8, big-endian gwei) ++ signature(96). The source address is implicit in the deposit signature.

func BuildBuilderExitCalldata

func BuildBuilderExitCalldata(pubkey []byte) ([]byte, error)

BuildBuilderExitCalldata builds the 48-byte builder exit request calldata (the builder pubkey). The source address is the transaction sender (msg.sender), which must match the builder's registered execution address.

func BuilderWithdrawalCredentials

func BuilderWithdrawalCredentials(walletAddress common.Address) [32]byte

BuilderWithdrawalCredentials builds the withdrawal credentials for an EIP-8282 builder deposit. Format: 0xB0 + 00...00 (11 zero bytes) + wallet_address (20 bytes).

func GweiToWei

func GweiToWei(gwei uint64) *big.Int

GweiToWei converts Gwei to Wei.

func ReadQueueFee

func ReadQueueFee(
	ctx context.Context,
	reader contractReader,
	contract common.Address,
) (fee *big.Int, active bool, err error)

ReadQueueFee reads the current per-request queue fee (in wei) for a builder system contract. It returns active=false while the contract still holds the pre-fork excess inhibitor (i.e. before GLOAS_FORK_EPOCH), signalling the caller to wait. The returned fee prices in queueFeeHeadroom extra slots for safety.

It returns ErrContractNotDeployed when there is no code at the contract address: an empty account reads slot 0 as zero, which would otherwise look like an active contract with an empty queue, and requests sent there confirm as plain value transfers without ever reaching the beacon chain.

func ValidatorWithdrawalCredentials

func ValidatorWithdrawalCredentials(walletAddress common.Address) [32]byte

ValidatorWithdrawalCredentials builds the withdrawal credentials for the pre-Gloas early-onboarding deposit, which is submitted through the regular validator deposit contract. Format: 0xB0 + 00...00 (11 zero bytes) + wallet_address (20 bytes).

Types

type BalanceService

type BalanceService struct {
	// contains filtered or unexported fields
}

BalanceService monitors builder key balances and performs automatic top-ups. Balances come from the key registry, which resolves them from the same epoch snapshot for the whole key set, so monitoring hundreds of keys costs one pass rather than one beacon query per key.

func NewBalanceService

func NewBalanceService(
	cfg *config.Config,
	chainSvc chain.Service,
	registry *builder_keys.Registry,
	depositSvc *DepositService,
	log logrus.FieldLogger,
) *BalanceService

NewBalanceService creates a new balance service.

func (*BalanceService) CheckAndTopup

func (s *BalanceService) CheckAndTopup(ctx context.Context, key *builder_keys.Key) (uint64, error)

CheckAndTopup tops the key up when its balance is below the threshold. It returns the amount deposited in gwei, or 0 when no top-up was needed — the caller credits exactly that to the live balance rather than guessing it.

func (*BalanceService) GetEffectiveBalance

func (s *BalanceService) GetEffectiveBalance(key *builder_keys.Key) (uint64, error)

GetEffectiveBalance returns the key's spendable balance: the chain balance plus local adjustments (top-ups credited, revealed payments debited) minus the pending payments the beacon state still owes out of it.

func (*BalanceService) NeedsTopup

func (s *BalanceService) NeedsTopup(key *builder_keys.Key) (bool, uint64, error)

NeedsTopup checks whether a key needs a top-up and returns the required amount. It returns ErrBuilderExited for a key whose exit has been initiated: after the sweep zeroes the balance a top-up would otherwise trigger every cooldown, cycling funds wallet -> exited entry -> (64 epochs locked) -> wallet forever.

type BuilderState

type BuilderState struct {
	Pubkey            []byte
	Index             uint64
	IsRegistered      bool
	Balance           uint64 // Gwei
	DepositEpoch      uint64
	WithdrawableEpoch uint64
}

BuilderState represents the current state of a builder in the beacon chain.

type DepositService

type DepositService struct {
	// contains filtered or unexported fields
}

DepositService handles builder deposits and top-ups via the EIP-8282 builder deposit system contract. It is key-agnostic: every operation names the builder key it acts on, so one service serves the whole managed key set.

func NewDepositService

func NewDepositService(
	cfg *config.Config,
	chainSvc chain.Service,
	w *wallet.Wallet,
	log logrus.FieldLogger,
) (*DepositService, error)

NewDepositService creates a new deposit service.

func (*DepositService) CreateDeposit

func (s *DepositService) CreateDeposit(
	ctx context.Context, key *builder_keys.Key, amountGwei uint64,
) error

CreateDeposit creates and sends an EIP-8282 builder deposit transaction for the given key. It is also used for top-ups (which are simply additional deposits for the same pubkey).

Before submitting it reads the contract's current per-request queue fee and, when DepositMaxFeeGwei is set, returns ErrDepositFeeTooHigh if the fee exceeds the limit so the caller can delay and retry. The transaction value is stake + queue fee.

func (*DepositService) CreateDeposits added in v0.0.3

func (s *DepositService) CreateDeposits(
	ctx context.Context, keys []*builder_keys.Key, amountGwei uint64,
) ([]error, error)

CreateDeposits submits deposits for several keys as one batch and returns the per-key errors (nil for the keys that landed). Batching matters because every deposit is serialized on the same funding key: bringing a fleet up one confirmed transaction at a time costs a block per key.

The queue fee is read once for the whole batch — it is a property of the contract, not of a key — so a fee over the operator's limit defers the whole batch with a single error rather than per key.

func (*DepositService) CreateTopup

func (s *DepositService) CreateTopup(
	ctx context.Context, key *builder_keys.Key, amountGwei uint64,
) error

CreateTopup creates and sends a top-up transaction (an additional deposit).

func (*DepositService) IsBuilderRegistered

func (s *DepositService) IsBuilderRegistered(key *builder_keys.Key) (bool, *BuilderState, error)

IsBuilderRegistered checks if the given builder key is registered on the beacon chain.

type EarlyDepositService

type EarlyDepositService struct {
	// contains filtered or unexported fields
}

EarlyDepositService submits a pre-Gloas builder onboarding deposit via the regular validator deposit contract. Both paths use 0xB0 withdrawal credentials, but unlike the post-fork builder deposit (EIP-8282 predeploy, DOMAIN_BUILDER_DEPOSIT), an early deposit is signed with the validator deposit domain — i.e. it is an ordinary validator deposit that sits in the beacon state's pending_deposits queue and is converted into a builder at the Gloas fork boundary.

func NewEarlyDepositService

func NewEarlyDepositService(
	cfg *config.Config,
	chainSvc chain.Service,
	w *wallet.Wallet,
	log logrus.FieldLogger,
) (*EarlyDepositService, error)

NewEarlyDepositService creates a new early deposit service.

func (*EarlyDepositService) CreateEarlyDeposits added in v0.0.3

func (s *EarlyDepositService) CreateEarlyDeposits(
	ctx context.Context, keys []*builder_keys.Key, amountGwei uint64,
) ([]error, error)

CreateEarlyDeposits builds, signs and sends validator deposits for the given keys as one batch, returning the per-key errors (nil for the keys that landed).

The deposits use 0xB0 (BUILDER_WITHDRAWAL_PREFIX) withdrawal credentials pointing at the funding wallet and are signed with the validator deposit domain over GENESIS_FORK_VERSION. They do not race each other: they sit in the pending-deposit queue together and the Gloas transition converts them all.

func (*EarlyDepositService) HasPendingDeposit

func (s *EarlyDepositService) HasPendingDeposit(key *builder_keys.Key) bool

HasPendingDeposit reports whether the key's pubkey is already present in the beacon state's pending_deposits queue. It is used after a restart to avoid submitting a duplicate early deposit while a prior one is still waiting in the queue.

type ExitService

type ExitService struct {
	// contains filtered or unexported fields
}

ExitService handles builder exits via the EIP-8282 builder exit system contract.

Unlike a validator voluntary exit (a BLS-signed beacon message), a builder exit is an execution-layer transaction to the builder exit predeploy carrying only the builder pubkey as calldata. The source is the transaction sender (msg.sender), which must match the builder's registered execution address — i.e. the funding wallet that supplied the withdrawal credentials at deposit time. A per-request queue fee is paid as msg.value.

func NewExitService

func NewExitService(
	chainSvc chain.Service,
	w *wallet.Wallet,
	log logrus.FieldLogger,
) *ExitService

NewExitService creates a new exit service.

func (*ExitService) CreateExit

func (s *ExitService) CreateExit(ctx context.Context, key *builder_keys.Key) error

CreateExit submits a builder exit request transaction for the given key.

Exits always proceed regardless of the configured deposit fee limit (unlike deposits/top-ups, which are delayed when the fee is too high), so the operator can always withdraw. The queue fee is read from the contract and paid as msg.value.

type LifecycleEvent

type LifecycleEvent struct {
	Action  string // "deposit", "topup", "exit", "state_change", "waiting_gloas", "balance_topup"
	Message string // Human-readable description
	Status  string // "info", "success", "warning", "error"
}

LifecycleEvent represents a lifecycle action for UI logging.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager orchestrates builder lifecycle operations.

func NewManager

func NewManager(
	cfg *config.Config,
	clClient *beacon.Client,
	chainSvc chain.Service,
	registry *builder_keys.Registry,
	w *wallet.Wallet,
	log logrus.FieldLogger,
) (*Manager, error)

NewManager creates a new lifecycle manager.

func (*Manager) CheckAndTopup

func (m *Manager) CheckAndTopup(ctx context.Context, key *builder_keys.Key) error

CheckAndTopup tops the given key up when its balance is below the threshold.

func (*Manager) EnsureBuilderRegistered

func (m *Manager) EnsureBuilderRegistered(ctx context.Context, key *builder_keys.Key) error

EnsureBuilderRegistered checks whether the given builder key is registered and deposits if needed. This is the synchronous version used by CLI commands (e.g. cmd/deposit.go) and the lifecycle API.

func (*Manager) GetBuilderState

func (m *Manager) GetBuilderState() *BuilderState

GetBuilderState returns the current builder state.

func (*Manager) GetPaymentTracker added in v0.0.2

func (m *Manager) GetPaymentTracker() *payload_bidder.PaymentTracker

GetPaymentTracker returns the shared payment tracker.

func (*Manager) GetWallet

func (m *Manager) GetWallet() *wallet.Wallet

GetWallet returns the wallet instance.

func (*Manager) InitiateExit

func (m *Manager) InitiateExit(ctx context.Context, key *builder_keys.Key) error

InitiateExit submits a builder exit request for the given key via the builder exit system contract.

func (*Manager) IsEnabled

func (m *Manager) IsEnabled() bool

IsEnabled returns whether the lifecycle manager is enabled.

func (*Manager) Reconcile added in v0.0.3

func (m *Manager) Reconcile()

Reconcile requests an immediate reconcile pass. Called when the target key count changes so a UI edit acts within a second rather than at the next tick.

func (*Manager) Registry added in v0.0.3

func (m *Manager) Registry() *builder_keys.Registry

Registry returns the managed builder key set.

func (*Manager) SetDepositPendingCallback

func (m *Manager) SetDepositPendingCallback(cb func())

SetDepositPendingCallback sets the callback invoked when a deposit is submitted.

func (*Manager) SetEnabled

func (m *Manager) SetEnabled(enabled bool)

SetEnabled sets whether the lifecycle manager is actively managing the builder.

func (*Manager) SetEventCallback

func (m *Manager) SetEventCallback(cb func(*LifecycleEvent))

SetEventCallback sets the callback invoked when lifecycle events occur (for UI logging).

func (*Manager) SetPaymentTracker added in v0.0.2

func (m *Manager) SetPaymentTracker(payments *payload_bidder.PaymentTracker)

SetPaymentTracker sets the shared payment tracker for the balance service and stores it for direct access.

func (*Manager) SetRegistrationCallback

func (m *Manager) SetRegistrationCallback(cb func(index uint64))

SetRegistrationCallback sets the callback invoked when builder registration completes.

func (*Manager) Start

func (m *Manager) Start(ctx context.Context) error

Start starts the lifecycle manager with async registration and balance monitoring.

func (*Manager) Stop

func (m *Manager) Stop()

Stop stops the lifecycle manager.

func (*Manager) TopupKey added in v0.0.3

func (m *Manager) TopupKey(ctx context.Context, key *builder_keys.Key, amountGwei uint64) error

TopupKey submits a top-up deposit for the key regardless of its current balance — the operator asked for it explicitly. amountGwei of 0 uses the configured top-up amount.

func (*Manager) WaitForRegistration

func (m *Manager) WaitForRegistration(
	ctx context.Context, key *builder_keys.Key, timeout time.Duration,
) error

WaitForRegistration waits for the given builder key to be registered.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL