SmartOperator. BeraChef treats SmartOperator as the validator operator (beaconDepositContract.getOperator), so you do not call BeraChef with an EOA the way Manage Incentives Commission describes for a normal validator.
Set commission
COMMISSION_MANAGER_ROLE calls SmartOperator.queueValCommission(uint96 commission). That forwards to BeraChef.queueValCommission(pubkey, commission). BeraChef rejects anything above MAX_COMMISSION_RATE (2000 bps, 20%).
The queue is not live yet. After commissionChangeDelay blocks, anyone calls BeraChef.activateQueuedValCommission(pubkey). SmartOperator does not wrap that activate.
Direct reward allocation
REWARDS_ALLOCATION_MANAGER_ROLE calls SmartOperator.queueRewardsAllocation(startBlock, weights), which forwards to BeraChef.queueNewRewardAllocation. startBlock must be at least block.number + rewardAllocationBlockDelay. BeraChef only accepts the call from the pubkey’s reward allocator, or from the operator if none is set. VALIDATOR_ADMIN_ROLE can set the allocator with SmartOperator.setRewardAllocator.
The Distributor, not you, calls BeraChef.activateReadyQueuedRewardAllocation. Inactivity and fallback rules live on BeraChef; see Manage Reward Allocations for the current delay and inactivity span.
Set the activation threshold
VALIDATOR_ADMIN_ROLE calls SmartOperator.setMinEffectiveBalance, which calls StakingPool.setMinEffectiveBalance. The pool reverts InvalidMinEffectiveBalance unless the value is at least MIN_EFFECTIVE_BALANCE (250,000 BERA) and strictly below MAX_EFFECTIVE_BALANCE (10,000,000 BERA). If you never set it, minEffectiveBalance() returns 250,000 BERA.
When totalDeposits + bufferedAssets first reaches that floor, StakingPool sets activeThresholdReached and records _validatorActivationBlock.
A later withdrawal whose consensus-layer portion would leave totalDeposits below minEffectiveBalance() triggers a full exit (_triggerFullExit).
Set the protocol fee
PROTOCOL_FEE_MANAGER_ROLE calls SmartOperator.setProtocolFeePercentage. The cap is MAX_PROTOCOL_FEE (2000 bps, 20%). The setter runs BGT and WBERA fee accrual, then stores the new rate.
accrueEarnedWBERAFees and accrueEarnedBGTFees are public, whenNotFullyExited, and have no role. Accrual also runs inside withdrawRewards, pullBeraToWithdrawalVault, notifyWithdrawalRequest, fullExitQueueDropBoost, and setProtocolFeePercentage.
Automatic WBERA flows
You cannot call the two functions that move WBERA offSmartOperator:
withdrawRewards(amount)reverts unlessmsg.senderisStakingPool. It unwraps WBERA and callsStakingPool.receiveRewards.pullBeraToWithdrawalVault(amount)reverts unlessmsg.senderisWithdrawalVault. It unwraps WBERA and sends BERA to the vault.
StakingPool.submit(andreceive()) andprocessRewardscall_collectRewards, which pulls WBERA viawithdrawRewardswhen the deposit math includeswberaToCollect, then pulls BERA fromStakingRewardsVault.processRewardsiswhenNotPausedand has no role. It compounds without a user deposit.WithdrawalVault._withdrawreadsavailableWBERABalance(), floors that cover to 1 gwei, and passes it intonotifyWithdrawalRequest. The pool applies that cover only on the normal post-threshold partial path. Short-circuit and full-exit zero the cover and do not callpullBeraToWithdrawalVault. Full cover (remaining CL amount 0) refundsmsg.value. Partial cover requests only the remainder from the consensus layer._triggerFullExitcallsfullExitQueueDropBoost, then_collectRewardsfor the whole rewards-vault balance andavailableWBERABalance(), then sends the pool’s native balance toWithdrawalVault.
StakingPool.isActive (factory activate succeeded), activeThresholdReached, totalDeposits, bufferedAssets, and on SmartOperator: availableWBERABalance(), rebaseableWberaAmount(), getEarnedWBERAFeeState().
Withdrawal system
Stakers callWithdrawalVault.requestWithdrawal or requestRedeem. The vault mints a non-transferable NFT and stores requestBlock.
Liquidity:
- Short-circuit (
!activeThresholdReached): BERA comes only frombufferedAssets. If the buffer is short,notifyWithdrawalRequestrevertsWithdrawalNotAllowed. No EIP-7002 request; the vault refunds the fee. - Post-threshold:
notifyWithdrawalRequestrevertsWithdrawalNotAlloweduntilblock.number >= _validatorActivationBlock + ENABLE_WITHDRAWAL_COOLDOWN_BLOCKS(129,600). After that, uncovered amount goes to the consensus layer; operator WBERA may cover as above. - Fully exited:
isFullyExitedon the pool, or_isFullyExited[pubkey]on the vault. BERA is already in the vault. No new CL request; the fee is refunded.
finalizeWithdrawalRequest / finalizeWithdrawalRequests revert InvalidSender unless msg.sender is request.user, and RequestNotReady until requestBlock + WITHDRAWAL_REQUEST_FINALIZATION_BLOCK_DELAY (129,600). Cover does not shorten that delay.
retryFullExit(pubkey, maxFeeToPay) is permissionless and payable. It reverts NotFullyExited unless the vault has marked the pubkey fully exited, and PendingWithdrawalInFlight if a partial withdrawal was requested in the last WITHDRAWAL_FLIGHT_BLOCK_DELAY (49,153) blocks. It re-submits an EIP-7002 full exit (amount 0). Staker requests do not use this path.
Building your front-end
Show finalize time fromgetWithdrawalRequest(requestId) (requestBlock + 129600). Only request.user can finalize. Use previewRedeem(shares) on the pool for share price. Batch with finalizeWithdrawalRequests.
Copy the React example in the guides repository. Fill config.example.json with the pool address and validator pubkey from install.sh or factory getCoreContracts.
Deprecated BGT entry points
These stay onSmartOperator with @deprecated in the interface. They do not wrap a staking-pool-specific yield path:
Roles
DEFAULT_ADMIN_ROLE is governance on initialize. VALIDATOR_ADMIN_ROLE is the role-admin for the rest:
Addresses and ABIs: Look up staking pool contract addresses.