Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

InfraredBERAWithdrawor

Git Source

Inherits: Upgradeable, IInfraredBERAWithdrawor

Manages withdrawal requests for BERA tokens from the consensus layer in the Infrared liquid staking protocol.

Assumes BERA is returned via the EIP-7002 withdrawal precompile and credited to the contract. Inherits from Upgradeable for governance and upgradability.

Tickets start at requestId = 1, and requestLength is incremented before assignment.

State Variables

WITHDRAW_PRECOMPILE

The address of the Withdraw Precompile settable in the next upgrade.

address public WITHDRAW_PRECOMPILE;

InfraredBERA

The address of the InfraredBERA.sol contract.

address public InfraredBERA;

requestLength

The current number of withdrawal requests queued (next requestId to be assigned).

uint256 public requestLength;

requests

Mapping of request IDs to withdrawal request tickets.

Key is the requestId (starting at 1), and value is the WithdrawalRequest struct.

mapping(uint256 => WithdrawalRequest) public requests;

requestsFinalisedUntil

The highest requestId that has been finalized by process.

uint256 public requestsFinalisedUntil;

totalClaimable

Total amount of BERA (in wei) marked as claimable (in PROCESSED state, non-depositor tickets).

uint256 public totalClaimable;

minActivationBalance

Minimum balance for a validator to stay active.

uint256 public minActivationBalance;

pendingWithdrawalsLength

Length of pending withdrawals queue

uint256 public pendingWithdrawalsLength;

nonceProcess

holding slot for withdraworLite storage var

uint256 public nonceProcess;

pendingWithdrawals

Mapping to track individual pending withdrawal amounts

mapping(uint256 => PendingWithdrawal) public pendingWithdrawals;

__gap

Reserve storage slots for future upgrades for safety

uint256[39] private __gap;

Functions

initializeV2

Initializes the contract for the next version, setting the withdrawal precompile address.

Only callable by the governance role (onlyGovernor). Reverts if _withdraw_precompile is zero.

function initializeV2(address _withdraw_precompile) external onlyGovernor;

Parameters

NameTypeDescription
_withdraw_precompileaddressThe address of the EIP-7002 withdrawal precompile.

reserves

Returns the amount of BERA available for new withdrawals (excluding claimable funds).

function reserves() public view returns (uint256);

Returns

NameTypeDescription
<none>uint256The contract’s balance minus totalClaimable (in wei).

getFee

Retrieves the current fee required by the withdrawal precompile.

Performs a static call to the precompile. Reverts if the call fails or the response is invalid (not 32 bytes).

function getFee() public view returns (uint256 fee);

Returns

NameTypeDescription
feeuint256The fee (in wei) required for a withdrawal request.

getQueuedAmount

Returns the total amount of BERA queued for withdrawal across all unprocessed tickets.

Calculates the difference between the cumulative amount at requestLength and requestsFinalisedUntil.

Returns 0 if requestLength == requestsFinalisedUntil (no unprocessed tickets) or requestLength == 0 (no tickets queued).

Assumes tickets from requestsFinalisedUntil + 1 to requestLength are in QUEUED state, as enforced by process.

function getQueuedAmount() public view returns (uint256 queuedAmount);

Returns

NameTypeDescription
queuedAmountuint256The total amount of BERA (in wei) in QUEUED tickets from requestsFinalisedUntil + 1 to requestLength.

getRequestsToProcess

Calculates the highest request ID that can be finalized by process given the current reserves.

Iterates through unprocessed tickets (requestsFinalisedUntil + 1 to requestLength) to find the maximum number of requests whose cumulative amount does not exceed reserves().

Returns requestsFinalisedUntil if no additional tickets can be processed due to insufficient reserves.

function getRequestsToProcess()
    external
    view
    returns (uint256 newRequestsFinalisedUntil);

Returns

NameTypeDescription
newRequestsFinalisedUntiluint256The highest requestId (inclusive) that can be processed without exceeding available reserves, or 0 if no tickets can be processed.

getTotalPendingWithdrawals

Sums all current pending withdrawals as helper for keeper to calculate how much needs to be executed next

Iterates through pending withdrawals, counting only those that have not expired (fulfilled)

function getTotalPendingWithdrawals(bytes32 pubkeyHash)
    public
    view
    returns (uint256 total);

Parameters

NameTypeDescription
pubkeyHashbytes32keccak256 of public key for validator to get pending withdrawals for

Returns

NameTypeDescription
totaluint256Sum amount in bera, pending on CL to return to contract

queue

Queues a withdrawal request for BERA from the consensus layer.

*Only callable by the InfraredBERA contract or a keeper. Reverts if:

  • Caller is unauthorized (msg.sender is neither InfraredBERA nor a keeper).
  • Receiver is invalid (keeper cannot queue for non-depositor, non-keeper cannot queue for depositor).
  • Amount is zero for non-depositor or exceeds InfraredBERA.confirmed() balance.*
function queue(address receiver, uint256 amount)
    external
    whenNotPaused
    returns (uint256 requestId);

Parameters

NameTypeDescription
receiveraddressThe address to receive the withdrawn BERA (user or depositor for rebalancing).
amountuint256The amount of BERA to withdraw (in wei).

Returns

NameTypeDescription
requestIduint256The unique ID assigned to the withdrawal request (starts at 1).

execute

Executes a withdrawal request via the EIP-7002 precompile.

*Rules for withdrawals:

  • Pass 0 amount for full exit
  • If not full exit withdrawal amount must leave validator in active state, which means stake must remain above 250k*

*Only callable by a keeper (onlyKeeper). Reverts if:

  • Exceeds validator stake, is not divisible by 1 gwei, or exceeds uint64 max.
  • Precompile call fails or returns invalid fee data.
  • Provided msg.value is less than the precompile fee.*

Refunds excess msg.value to the keeper after the precompile call.

*References:

  • https://eips.ethereum.org/EIPS/eip-7002
  • https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4788.md*
function execute(
    BeaconRootsVerify.BeaconBlockHeader calldata header,
    BeaconRootsVerify.Validator calldata validator,
    bytes32[] calldata validatorMerkleWitness,
    bytes32[] calldata balanceMerkleWitness,
    uint256 validatorIndex,
    bytes32 balanceLeaf,
    uint256 amount,
    uint256 nextBlockTimestamp
) external payable onlyKeeper whenNotPaused;

Parameters

NameTypeDescription
headerBeaconRootsVerify.BeaconBlockHeaderThe Beacon block header data
validatorBeaconRootsVerify.ValidatorThe full validator struct to deposit for
validatorMerkleWitnessbytes32[]Merkle witness for validator
balanceMerkleWitnessbytes32[]Merkle witness for balance container
validatorIndexuint256index of validator in beacon state tree
balanceLeafbytes3232 bytes chunk including packed balance
amountuint256The amount of BERA to withdraw (in wei).
nextBlockTimestampuint256timestamp of following block to header to verify parent root in beaconroots call

process

Finalizes a range of withdrawal requests, marking them as claimable or rebalancing to the depositor.

*Reverts if:

  • newRequestsFinalisedUntil exceeds requestLength.
  • newRequestsFinalisedUntil is less than or equal to requestsFinalisedUntil.
  • Available reserves are insufficient for the total amount to finalize.*

Accumulates amounts for depositor rebalancing into a single call to InfraredBERADepositor.queue.

Updates totalClaimable for non-depositor tickets.

function process(uint256 newRequestsFinalisedUntil)
    external
    onlyKeeper
    whenNotPaused;

Parameters

NameTypeDescription
newRequestsFinalisedUntiluint256The highest requestId to finalize (inclusive).

claim

Claims a finalized withdrawal request for a user.

*Reverts if:

  • requestId exceeds requestsFinalisedUntil (not finalized).
  • sender is not receiver or keeper
  • Ticket is not in PROCESSED state or belongs to the depositor.*

Transitions the ticket to CLAIMED and transfers the amount to the receiver.

function claim(uint256 requestId) external whenNotPaused;

Parameters

NameTypeDescription
requestIduint256The ID of the withdrawal request to claim.

claimBatch

Claims multiple finalized withdrawal requests for same receiver in a single transaction.

*Reverts if:

  • Any requestId exceeds requestsFinalisedUntil (not finalized).
  • Any ticket receiver is not the same as the others
  • Any ticket is not in PROCESSED state or belongs to the depositor.
  • sender is not receiver or keeper*

Transitions each ticket to CLAIMED and transfers the total amount to the caller.

Emits a Claimed event for each claimed ticket.

function claimBatch(uint256[] calldata requestIds, address receiver)
    external
    whenNotPaused;

Parameters

NameTypeDescription
requestIdsuint256[]An array of request IDs to claim.
receiveraddressrecipient address of all requestIds

sweepForcedExit

Handles Forced withdrawals from the CL.

*RESTRICTED USAGE: This function should ONLY be called when:

  • A validator has been forced to exit from the CL.*

The funds will enter the IBERA system as a deposit via the InfraredBERADepositor.

function sweepForcedExit(
    BeaconRootsVerify.BeaconBlockHeader calldata header,
    BeaconRootsVerify.Validator calldata validator,
    uint256 validatorIndex,
    bytes32[] calldata validatorMerkleWitness,
    uint256 nextBlockTimestamp
) external onlyGovernor;

Parameters

NameTypeDescription
headerBeaconRootsVerify.BeaconBlockHeaderThe Beacon block header data
validatorBeaconRootsVerify.ValidatorThe full validator struct to deposit for
validatorIndexuint256index of validator
validatorMerkleWitnessbytes32[]Merkle witness for validator
nextBlockTimestampuint256timestamp of following block to header to verify parent root in beaconroots call

sweepUnaccountedForFunds

Handles excess stake that was refunded from a validator due to non-IBERA (bypass) deposits exceeding MAX_EFFECTIVE_BALANCE

*RESTRICTED USAGE: This function should ONLY be called when:

  • A non-IBERA entity deposits to our validator, pushing total stake above MAX_EFFECTIVE_BALANCE
  • The excess stake is refunded by the CL to this contract*

The funds will enter the IBERA system as yield via the FeeReceivor

*This should NEVER be used for:

  • Validators exited due to falling out of the validator set*

Note: access: Only callable by governance

function sweepUnaccountedForFunds(uint256 amount) external onlyGovernor;

Parameters

NameTypeDescription
amountuint256The amount of excess stake to sweep

receive

receive() external payable;

setMinActivationBalance

function setMinActivationBalance(uint256 _minActivationBalance)
    external
    onlyGovernor;

totalPendingWithdrawals

Internal function to get total pending withdrawals over all validators and remove outdated ones

function totalPendingWithdrawals() internal returns (uint256 total);