OllaGovernance
What is OllaGovernance?
OllaGovernance is the protocol's timelock and the on-chain admin of every other contract. It inherits OpenZeppelin's TimelockControllerUpgradeable and is itself UUPS-upgradeable.
Two distinct entities use the same name in conversation, but they are not the same:
- The OllaGovernance contract: a single deployed proxy address. This is the
owner()ofOllaCoreandOllaVault, theDEFAULT_ADMIN_ROLEholder on every satellite, and the upgrade authority for the proxies it controls. - The governance admin: a multisig wallet held by the team / DAO that holds the timelock's
PROPOSER_ROLE,EXECUTOR_ROLE, andCANCELLER_ROLE. It is the wallet that operates the timelock. Tracked on-chain asgovernanceAdmin.
Transferring governance changes the admin wallet, not the contract address. Satellite admin pointers do not need to move during a transfer.
Key responsibilities:
- Timelocking parameter changes and upgrades.
- Wrapping setters on OllaCore, OllaVault, and SafetyModule as
onlySelfpassthroughs (so they can only be invoked via the timelock execute flow). - Holding the treasury address and routing it to fee distribution.
- Two-step governance handover (
proposeGovernancethenacceptGovernance). - Emergency
pause()/unpause()of OllaCore and OllaVault that bypass the timelock.
How a setter actually fires
Every parameter change happens in three steps:
- The governance admin wallet calls
schedule(...)on the timelock with calldata that targetsOllaGovernanceitself, e.g.setProtocolFeeBP(newFeeBP). - The timelock waits
minDelay. - The governance admin wallet calls
execute(...)on the timelock. The timelock makes the internal call toOllaGovernance.setProtocolFeeBP(newFeeBP)withmsg.sender == address(this). TheonlySelfmodifier passes only because of this self-call. OllaGovernance.setProtocolFeeBPthen forwards toOllaCore.setProtocolFeeBP, which checksonlyOwner(owner() == OllaGovernance) and applies the change.
The end result: every parameter change waits through the timelock, no matter which contract it ultimately mutates.
Where setters live
Some settings live on OllaGovernance as wrappers; others live directly on the target contract and are invoked by scheduling a direct timelock action targeting that contract.
| Parameter | Wrapped by OllaGovernance (timelocked) | Lives on |
|---|---|---|
setProtocolFeeBP | yes | OllaCore |
setTreasuryFeeSplitBP | yes | OllaCore |
setRebalanceGasThreshold | yes | OllaCore (also propagates to StakingManager) |
setRebalanceCooldown | yes | OllaCore |
setSafetyModule | yes | OllaCore (replace the module without UUPS) |
setDepositCap | yes | SafetyModule |
setWithdrawalMinimum | yes | SafetyModule |
setMinRateDropBps | yes | SafetyModule |
setMaxQueueRatioBps | yes | SafetyModule |
setMaxAccountingDelay | yes | SafetyModule |
setTreasury | yes | OllaGovernance |
recoverStAztec | yes | OllaVault |
reconcileBufferedAssets | yes | OllaVault |
setRateHighWaterMark | no, schedule a direct call | SafetyModule |
removeDrainedRewardRollup | no, schedule a direct call | StakingManager |
setProviderRewardsRecipient | no, staking-provider role, not governance | StakingProviderRegistry |
setVault | one-time setVault (onlyOwner) | OllaCore |
setCore | one-time setCore (DEFAULT_ADMIN_ROLE) | OllaGovernance |
Rebalance derives the buffer target from OllaVault.pendingWithdrawalAssets() directly, unstaking enough to cover the queue and staking everything else.
Methods
Emergency
Callable directly by the governance admin, not timelocked, for rapid incident response. Restricted by if (msg.sender != governanceAdmin) revert.
function emergencyPauseAll() external
function emergencyUnpauseAll() external
emergencyPauseAll() calls pause() on both OllaCore and OllaVault. The SafetyModule is intentionally not paused. It is only reachable through Core and Vault, both of which are paused, so its independent pause is unnecessary.
Timelocked passthroughs (onlySelf)
Each of these reverts unless invoked via the timelock execute flow.
// OllaCore parameters
function setProtocolFeeBP(uint256 newFeeBP) external
function setTreasuryFeeSplitBP(uint256 newSplitBP) external
function setRebalanceGasThreshold(uint256 newThreshold) external
function setRebalanceCooldown(uint256 cooldown_) external
function setSafetyModule(address newSafetyModule) external
// OllaVault operations
function recoverStAztec(address recipient, uint256 amount) external
function reconcileBufferedAssets() external
// SafetyModule parameters
function setDepositCap(uint256 cap) external
function setWithdrawalMinimum(uint256 minShares) external
function setMinRateDropBps(uint256 bps) external
function setMaxQueueRatioBps(uint256 bps) external
function setMaxAccountingDelay(uint256 delay) external
// Treasury / governance handover
function setTreasury(address newTreasury) external
function proposeGovernance(address newGovernance) external
function cancelGovernanceProposal() external
Upgrades (onlySelf)
Both functions accept optional initialization calldata. Pass "" for a no-op upgrade or ABI-encoded initializer calldata for atomic post-upgrade init.
function upgradeCore(address newImplementation, bytes calldata data) external
function upgradeSatellite(address proxy, address newImplementation, bytes calldata data) external
OllaGovernance itself upgrades via the standard UUPS path (upgradeToAndCall), gated by _authorizeUpgrade which is onlySelf. Even self-upgrades go through the timelock.
Setup
function setCore(address core_) external // DEFAULT_ADMIN_ROLE, one-time
A pointer to OllaCore is required for the passthrough setters to know where to forward. setCore can only succeed once.
Governance handover
Two-step transfer to prevent typos sending governance into a black hole:
function proposeGovernance(address newGovernance) external // onlySelf (timelocked)
function acceptGovernance() external // direct call by the new admin
function cancelGovernanceProposal() external // onlySelf (timelocked)
When the new admin calls acceptGovernance():
PROPOSER_ROLE,EXECUTOR_ROLE,CANCELLER_ROLE, andDEFAULT_ADMIN_ROLEon the timelock are granted to the new admin and revoked from the old admin.- The on-chain
governanceAdminpointer is updated. - Satellite contracts are not touched. They were granted
DEFAULT_ADMIN_ROLEto the OllaGovernance contract address (not to the admin wallet), so a wallet handover does not change who can upgrade or admin them.
View methods
function core() external view returns (address) // managed OllaCore
function treasury() external view returns (address) // current treasury
function pendingGovernance() external view returns (address)
function governanceAdmin() external view returns (address) // current admin wallet
Events
event TreasuryUpdated(address indexed oldTreasury, address indexed newTreasury)
event GovernanceTransferProposed(address indexed proposer, address indexed newGovernance)
event GovernanceTransferAccepted(address indexed oldGovernance, address indexed newGovernance)
event GovernanceTransferCancelled(address indexed pendingGovernance)
event EmergencyPauseAll()
event EmergencyUnpauseAll()
event CoreSet(address indexed core)