The PufferProtocol
serves as the central contract and fulfills three key functions:
-
Validator State Management: Including overseeing accounting processes, as well as handling the custody of validator bonds and Validator Tickets VTs.
-
Validator Interactions: Including the registration of new validators and managing their exit from the protocol.
-
Routing ETH: The protocol directs ETH from the ER4626 vault to EigenPods to fund natively restaked Ethereum validators.
- Initial Deposit and Minting: (Not pictured) Stakers deposit ETH into the
PufferVault
to mint pufETH. This ETH funds Ethereum staking and EigenLayer restaking. Similarly, Node Operators (NoOps) use pufETH for their bonds. - Validator Registration: NoOps submit a transaction to register their validator, locking in their bond and VTs.
- Verification by Guardians: The Guardians perform an off-chain verification to confirm the registration's validity.
- Requesting Funds: The
PufferProtocol
contract requests 32 ETH from thePufferVault
. - Transfer to PufferModule: The requested 32 ETH is transferred to a
PufferModule
. - Initiating Validator Deposit: The
PufferProtocol
proceeds to initiate a validator deposit. - Deposit Request to EigenLayer: The
PufferModule
requests a validator deposit from EigenLayer. - EigenLayer Deposit Initiation: EigenLayer initiates the deposit process.
- Beacon Chain Interaction: The 32 ETH along with the validator deposit message are sent to the Beacon chain deposit contract.
- Completion and Restaking: Once the Beacon chain recognizes the validator, a withdrawal credentials merkle proof is submitted back to EigenLayer to enable the restaking of the validator's ETH.
NoOps are required to deposit pufETH and Validator Tickets (VTs) to register a validator. The amount of pufETH depends on the use of an anti-slasher enclave:
- With an enclave: 1 ETH worth of pufETH is required.
- Without an enclave: 2 ETH worth of pufETH is required.
The PufferProtocol
contract mandates a minimum number of VTs at registration, set at minimumVtAmount = 28
. This minimum commitment, equivalent to a 4-week operation, ensures efficient allocation of staker ETH. VTs can be minted from the ValidatorTicket contract or obtained from the secondary market as they are ERC20s.
function registerValidatorKey( ValidatorKeyData calldata data, bytes32 moduleName, Permit calldata pufETHPermit, Permit calldata vtPermit )
The NoOp must supply the following ValidatorKeyData
struct created off-chain:
-
bytes blsPubKey
: BLS public key generated by the NoOp. -
bytes signature
: Signature of the deposit data by the validator's BLS private key. -
bytes32 depositDataRoot
: Root hash of the deposit data. -
bytes[] blsEncryptedPrivKeyShares
: Array of encrypted private key shares for each Guardian. -
bytes blsPubKeySet
: Used to reconstruct the BLS public key shares. -
bytes raveEvidence
: Remote attestation evidence if using an enclave, checked for validity by the Guardians.
Additional details to supply:
bytes32 moduleName
: Name of thePufferModule
for which the validator is registering. ThePufferModule
must be registered with thePufferProtocol
contract (created via thePufferModuleManager
factory).pufETHPermit
andvtPermit
: Permit data for pufETH andValidatorTicket
tokens. Direct ETH can be sent for minting or existing tokens can be approved for transfer.
This function allows flexibility in how pufETH and VTs are supplied. Options include:
- Sending ETH directly for minting pufETH and VTs.
- Transferring pufETH and VTs using Permit messages or prior approve transactions.
- Combining both methods (e.g., minting pufETH and transferring VTs).
Successful registration adds the validator to the PufferModule
queue. Guardians verify the data and manage keyshare custody. Once verified, they provision the validator with 32 ETH from the PufferVault
to deploy to the PufferModule's
EigenPod
.
Once a quorum of Guardians verifies the validity of a registration, the provisionNode
function is called to provision the validator with 32 ETH. The provisioning process will proceed if the following conditions are met:
- The registration data, including withdrawal credentials and deposit message, is validated.
- The
PufferVault
has sufficient liquidity (at least 32 ETH available). - The BLS public key has not been previously used.
- The RAVE evidence is valid (for enclave validators).
- The
PufferModule
has capacity to accommodate the new validator. - If any registration is invalid, Guardians can utilize the
skipProvisioning
function on thePufferProtocol
. This action dequeues the validator's registration, refunds their bond, and burnsvtPenalty
VTs to deter attempts to grief the protocol.
The provisionNode
function executes several critical steps in one atomic transaction:
- It orchestrates the transfer of 32 ETH from the
PufferVault
to thePufferModule
, then to theEigenPod
, and finally to the Beacon Chain Deposit contract. - It updates the
_numberOfActivePufferValidators
counter on thePufferOracleV2
contract, reflecting the addition of a new validator on the beacon chain.
- The provisioning of a validator involves transferring 32 ETH out of the PufferVault, which could negatively affect the pufETH:ETH conversion rate. To prevent this, the vault calculation includes the ETH amount locked as reported by
PUFFER_ORACLE.getLockedEthAmount()
. When provisioning occurs, the oracle contract’s reported locked ETH amount is increased by 32 ETH. This adjustment ensures that the vault's exchange rate remains unchanged, despite the outflow of funds. For a more detailed explanation, refer to thePufferOracleV2
documentation.
Once a validator is onboarded into a PufferModule
and their validator is observable from the Beacon chain, their Beacon chain ETH is restaked. This process involves delegating the validator's ETH to a RestakingOperator
, as determined by the DAO.
Restaking requires the submission of a Merkle proof to EigenLayer. This proof verifies that the validator's withdrawal credentials are correctly aligned with their designated EigenPod
. The responsibility of calculating and submitting these Merkle proofs lies with the Guardians.
Exiting a validator from the Puffer protocol involves broadcasting a signed voluntary exit message on the Beacon chain.
NoOps are free to initiate a voluntary exit at any point. However, if they choose to exit before the required minimumVtAmount
days have elapsed, their VT balance will be subject to penalties.
Guardians, utilizing their enclaves, are authorized to sign and broadcast voluntary exit messages for validators under specific circumstances:
- If a validator's balance falls below the
_ejectionThreshold
ETH, as defined in the GuardianModule. - If the Node Operator fails to replenish their VTs after their locked amount has expired.
Post-exit, the exited validator’s ETH will be redirected to the PufferModule's
EigenPod
. The PufferModuleManager
oversees the full withdrawal process on EigenLayer, which involves Merkle proofs and queueing, to transfer the ETH back to the PufferModule
.
The Guardians then execute batchHandleWithdrawals()
on the PufferProtocol
, which returns pufETH bonds to NoOps, burns their consumed VTs, and performs necessary accounting. The process addresses three potential scenarios:
Sufficient Withdrawal (withdrawalAmount ≥ 32 ETH):
- 32 ETH is transferred back to the
PufferVault
. - Any excess over 32 ETH remains in the
PufferModule
as rewards for the NoOp. - The NoOp is reimbursed their full bond amount.
Insufficient Withdrawal (withdrawalAmount < 32 ETH):
- The entire withdrawalAmount is transferred back to the
PufferVault
. - The missing ETH (32 ETH - withdrawalAmount) is burned from the NoOp’s bond.
- The NoOp receives the remainder of their bond, assuming losses due to inactivity penalties do not exceed the bond itself.
Validator Was Slashed:
- All withdrawn ETH is transferred to the
PufferVault
. - The entire bond is forfeited and burned, irrespective of the slashing amount.
In all scenarios, the _numberOfActivePufferValidators
count on the PufferOracleV2
contract is decremented atomically, reducing the locked ETH amount by 32 ETH per exited validator to maintain the pufETH conversion rate.
Each validator operated by a NoOp consumes one VT per day. While the getValidatorTicketsBalance()
function returns the total amount of VTs initially deposited by the NoOp, it does not reflect the real-time balance of VTs. This is due to the prohibitive gas costs associated with continually updating VT balances on-chain.
To efficiently manage VT consumption without incurring high on-chain costs, Guardians track VT usage off-chain. NoOps can access up-to-date VT consumption information through frontend interfaces, which provide a clear view of their current VT status.
Maintaining active validators requires more than just the initial deposit of a minimum of 28 VTs at registration. To ensure continuous operation and prevent ejection from the network, NoOp must periodically top up their VT balance. This is crucial as running out of VTs could lead to a validator being deactivated.
NoOps can replenish their VT supply by executing the depositValidatorTickets(permit, nodeOperator)
function. This allows them to add VTs to their account, ensuring their validators can continue to operate without interruption. Note that the PufferProtocol
tracks validators by wallet address, so only one function call is needed to top up VTs across all of your validators.
It's important for NoOps to monitor their VT consumption regularly and respond proactively to avoid disruptions in their validator operations.
Since VT consumption is tracked off-chain, withdrawing excess VTs, withdrawValidatorTickets
can only be called when the NoOp has no active or pending validators. In future protocol upgrades, ZKPs will be used to allow VTs to be withdrawn while validators are still active.
In the Puffer protocol, NoOps receive 100% of the consensus and execution rewards generated by their validators. This arrangement acknowledges the upfront commitment made by NoOps when purchasing VTs.
When NoOps employ tools like MEV-Boost, execution rewards are directly sent to their designated wallet addresses. This is specified through the fee recipient
parameter. Unlike other protocols there is no need to share these rewards with the protocol.
Consensus rewards are directed to the validators’ withdrawal credentials, which are linked to EigenPods
. These rewards accumulate and, following an upcoming EigenLayer upgrade that improves partial withdrawal gas-efficiency, will be accessible for claiming through the PufferModules.
Beyond the direct rewards from consensus and execution, Puffer validators also benefit from a share of the protocol's restaking rewards. Similar to consensus rewards, these restaking rewards are set to become claimable in future updates to EigenLayer, enhancing the overall profitability and incentive for NoOps within the Puffer ecosystem.