// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.24; import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol"; import { IProtocolFeeController } from "./IProtocolFeeController.sol"; import { IAuthorizer } from "./IAuthorizer.sol"; import { IVault } from "./IVault.sol"; /** * @notice Interface for functions defined on the `VaultAdmin` contract. * @dev `VaultAdmin` is the Proxy extension of `VaultExtension`, and handles the least critical operations, * as two delegate calls add gas to each call. Most of the permissioned calls are here. */ interface IVaultAdmin { /******************************************************************************* Constants and immutables *******************************************************************************/ /** * @notice Returns the main Vault address. * @dev The main Vault contains the entrypoint and main liquidity operation implementations. * @return vault The address of the main Vault */ function vault() external view returns (IVault); /** * @notice Returns the Vault's pause window end time. * @dev This value is immutable, and represents the timestamp after which the Vault can no longer be paused * by governance. Balancer timestamps are 32 bits. * * @return pauseWindowEndTime The timestamp when the Vault's pause window ends */ function getPauseWindowEndTime() external view returns (uint32 pauseWindowEndTime); /** * @notice Returns the Vault's buffer period duration. * @dev This value is immutable. It represents the period during which, if paused, the Vault will remain paused. * This ensures there is time available to address whatever issue caused the Vault to be paused. Balancer * timestamps are 32 bits. * * @return bufferPeriodDuration The length of the buffer period in seconds */ function getBufferPeriodDuration() external view returns (uint32 bufferPeriodDuration); /** * @notice Returns the Vault's buffer period end time. * @dev This value is immutable. If already paused, the Vault can be unpaused until this timestamp. Balancer * timestamps are 32 bits. * * @return bufferPeriodEndTime The timestamp after which the Vault remains permanently unpaused */ function getBufferPeriodEndTime() external view returns (uint32 bufferPeriodEndTime); /** * @notice Get the minimum number of tokens in a pool. * @dev We expect the vast majority of pools to be 2-token. * @return minTokens The minimum token count of a pool */ function getMinimumPoolTokens() external pure returns (uint256 minTokens); /** * @notice Get the maximum number of tokens in a pool. * @return maxTokens The maximum token count of a pool */ function getMaximumPoolTokens() external pure returns (uint256 maxTokens); /** * @notice Get the minimum total supply of pool tokens (BPT) for an initialized pool. * @dev This prevents pools from being completely drained. When the pool is initialized, this minimum amount of BPT * is minted to the zero address. This is an 18-decimal floating point number; BPT are always 18 decimals. * * @return poolMinimumTotalSupply The minimum total supply a pool can have after initialization */ function getPoolMinimumTotalSupply() external pure returns (uint256 poolMinimumTotalSupply); /** * @notice Get the minimum total supply of an ERC4626 wrapped token buffer in the Vault. * @dev This prevents buffers from being completely drained. When the buffer is initialized, this minimum number * of shares is added to the shares resulting from the initial deposit. Buffer total supply accounting is internal * to the Vault, as buffers are not tokenized. * * @return bufferMinimumTotalSupply The minimum total supply a buffer can have after initialization */ function getBufferMinimumTotalSupply() external pure returns (uint256 bufferMinimumTotalSupply); /** * @notice Get the minimum trade amount in a pool operation. * @dev This limit is applied to the 18-decimal "upscaled" amount in any operation (swap, add/remove liquidity). * @return minimumTradeAmount The minimum trade amount as an 18-decimal floating point number */ function getMinimumTradeAmount() external view returns (uint256 minimumTradeAmount); /** * @notice Get the minimum wrap amount in a buffer operation. * @dev This limit is applied to the wrap operation amount, in native underlying token decimals. * @return minimumWrapAmount The minimum wrap amount in native underlying token decimals */ function getMinimumWrapAmount() external view returns (uint256 minimumWrapAmount); /******************************************************************************* Vault Pausing *******************************************************************************/ /** * @notice Indicates whether the Vault is paused. * @dev If the Vault is paused, all non-Recovery Mode state-changing operations on pools will revert. Note that * ERC4626 buffers and the Vault have separate and independent pausing mechanisms. Pausing the Vault does not * also pause buffers (though we anticipate they would likely be paused and unpaused together). Call * `areBuffersPaused` to check the pause state of the buffers. * * @return vaultPaused True if the Vault is paused */ function isVaultPaused() external view returns (bool vaultPaused); /** * @notice Returns the paused status, and end times of the Vault's pause window and buffer period. * @dev Balancer timestamps are 32 bits. * @return vaultPaused True if the Vault is paused * @return vaultPauseWindowEndTime The timestamp of the end of the Vault's pause window * @return vaultBufferPeriodEndTime The timestamp of the end of the Vault's buffer period */ function getVaultPausedState() external view returns (bool vaultPaused, uint32 vaultPauseWindowEndTime, uint32 vaultBufferPeriodEndTime); /** * @notice Pause the Vault: an emergency action which disables all operational state-changing functions on pools. * @dev This is a permissioned function that will only work during the Pause Window set during deployment. * Note that ERC4626 buffer operations have an independent pause mechanism, which is not affected by pausing * the Vault. Custom routers could still wrap/unwrap using buffers while the Vault is paused, unless buffers * are also paused (with `pauseVaultBuffers`). */ function pauseVault() external; /** * @notice Reverse a `pause` operation, and restore Vault pool operations to normal functionality. * @dev This is a permissioned function that will only work on a paused Vault within the Buffer Period set during * deployment. Note that the Vault will automatically unpause after the Buffer Period expires. As noted above, * ERC4626 buffers and Vault operations on pools are independent. Unpausing the Vault does not reverse * `pauseVaultBuffers`. If buffers were also paused, they will remain in that state until explicitly unpaused. */ function unpauseVault() external; /******************************************************************************* Pool Pausing *******************************************************************************/ /** * @notice Pause the Pool: an emergency action which disables all pool functions. * @dev This is a permissioned function that will only work during the Pause Window set during pool factory * deployment. * * @param pool The pool being paused */ function pausePool(address pool) external; /** * @notice Reverse a `pause` operation, and restore the Pool to normal functionality. * @dev This is a permissioned function that will only work on a paused Pool within the Buffer Period set during * deployment. Note that the Pool will automatically unpause after the Buffer Period expires. * * @param pool The pool being unpaused */ function unpausePool(address pool) external; /******************************************************************************* Fees *******************************************************************************/ /** * @notice Assigns a new static swap fee percentage to the specified pool. * @dev This is a permissioned function, disabled if the pool is paused. The swap fee percentage must be within * the bounds specified by the pool's implementation of `ISwapFeePercentageBounds`. * Emits the SwapFeePercentageChanged event. * * @param pool The address of the pool for which the static swap fee will be changed * @param swapFeePercentage The new swap fee percentage to apply to the pool */ function setStaticSwapFeePercentage(address pool, uint256 swapFeePercentage) external; /** * @notice Collects accumulated aggregate swap and yield fees for the specified pool. * @dev Fees are sent to the ProtocolFeeController address. * @param pool The pool on which all aggregate fees should be collected * @return swapFeeAmounts An array with the total swap fees collected, sorted in token registration order * @return yieldFeeAmounts An array with the total yield fees collected, sorted in token registration order */ function collectAggregateFees( address pool ) external returns (uint256[] memory swapFeeAmounts, uint256[] memory yieldFeeAmounts); /** * @notice Update an aggregate swap fee percentage. * @dev Can only be called by the current protocol fee controller. Called when governance overrides a protocol fee * for a specific pool, or to permissionlessly update a pool to a changed global protocol fee value (if the pool's * fee has not previously been set by governance). Ensures the aggregate percentage <= FixedPoint.ONE, and also * that the final value does not lose precision when stored in 24 bits (see `FEE_BITLENGTH` in VaultTypes.sol). * Emits an `AggregateSwapFeePercentageChanged` event. * * @param pool The pool whose swap fee percentage will be updated * @param newAggregateSwapFeePercentage The new aggregate swap fee percentage */ function updateAggregateSwapFeePercentage(address pool, uint256 newAggregateSwapFeePercentage) external; /** * @notice Update an aggregate yield fee percentage. * @dev Can only be called by the current protocol fee controller. Called when governance overrides a protocol fee * for a specific pool, or to permissionlessly update a pool to a changed global protocol fee value (if the pool's * fee has not previously been set by governance). Ensures the aggregate percentage <= FixedPoint.ONE, and also * that the final value does not lose precision when stored in 24 bits (see `FEE_BITLENGTH` in VaultTypes.sol). * Emits an `AggregateYieldFeePercentageChanged` event. * * @param pool The pool whose yield fee percentage will be updated * @param newAggregateYieldFeePercentage The new aggregate yield fee percentage */ function updateAggregateYieldFeePercentage(address pool, uint256 newAggregateYieldFeePercentage) external; /** * @notice Sets a new Protocol Fee Controller for the Vault. * @dev This is a permissioned call. Emits a `ProtocolFeeControllerChanged` event. * @param newProtocolFeeController The address of the new Protocol Fee Controller */ function setProtocolFeeController(IProtocolFeeController newProtocolFeeController) external; /******************************************************************************* Recovery Mode *******************************************************************************/ /** * @notice Enable recovery mode for a pool. * @dev This is a permissioned function. It enables a safe proportional withdrawal, with no external calls. * Since there are no external calls, ensuring that entering Recovery Mode cannot fail, we cannot compute and so * must forfeit any yield fees between the last operation and enabling Recovery Mode. For the same reason, live * balances cannot be updated while in Recovery Mode, as doing so might cause withdrawals to fail. * * @param pool The address of the pool */ function enableRecoveryMode(address pool) external; /** * @notice Disable recovery mode for a pool. * @dev This is a permissioned function. It re-syncs live balances (which could not be updated during * Recovery Mode), forfeiting any yield fees that accrued while enabled. It makes external calls, and could * potentially fail if there is an issue with any associated Rate Providers. * * @param pool The address of the pool */ function disableRecoveryMode(address pool) external; /******************************************************************************* Query Functionality *******************************************************************************/ /** * @notice Disables query functionality on the Vault. Can only be called by governance. * @dev The query functions rely on a specific EVM feature to detect static calls. Query operations are exempt from * settlement constraints, so it's critical that no state changes can occur. We retain the ability to disable * queries in the unlikely event that EVM changes violate its assumptions (perhaps on an L2). * This function can be acted upon as an emergency measure in ambiguous contexts where it's not 100% clear whether * disabling queries is completely necessary; queries can still be re-enabled after this call. */ function disableQuery() external; /** * @notice Disables query functionality permanently on the Vault. Can only be called by governance. * @dev Shall only be used when there is no doubt that queries pose a fundamental threat to the system. */ function disableQueryPermanently() external; /** * @notice Enables query functionality on the Vault. Can only be called by governance. * @dev Only works if queries are not permanently disabled. */ function enableQuery() external; /******************************************************************************* ERC4626 Buffers *******************************************************************************/ /** * @notice Indicates whether the Vault buffers are paused. * @dev When buffers are paused, all buffer operations (i.e., calls on the Router with `isBuffer` true) * will revert. Pausing buffers is reversible. Note that ERC4626 buffers and the Vault have separate and * independent pausing mechanisms. Pausing the Vault does not also pause buffers (though we anticipate they * would likely be paused and unpaused together). Call `isVaultPaused` to check the pause state of the Vault. * * @return buffersPaused True if the Vault buffers are paused */ function areBuffersPaused() external view returns (bool buffersPaused); /** * @notice Pauses native vault buffers globally. * @dev When buffers are paused, it's not possible to add liquidity or wrap/unwrap tokens using the Vault's * `erc4626BufferWrapOrUnwrap` primitive. However, it's still possible to remove liquidity. Currently it's not * possible to pause vault buffers individually. * * This is a permissioned call, and is reversible (see `unpauseVaultBuffers`). Note that the Vault has a separate * and independent pausing mechanism. It is possible to pause the Vault (i.e. pool operations), without affecting * buffers, and vice versa. */ function pauseVaultBuffers() external; /** * @notice Unpauses native vault buffers globally. * @dev When buffers are paused, it's not possible to add liquidity or wrap/unwrap tokens using the Vault's * `erc4626BufferWrapOrUnwrap` primitive. However, it's still possible to remove liquidity. As noted above, * ERC4626 buffers and Vault operations on pools are independent. Unpausing buffers does not reverse `pauseVault`. * If the Vault was also paused, it will remain in that state until explicitly unpaused. * * This is a permissioned call. */ function unpauseVaultBuffers() external; /** * @notice Initializes buffer for the given wrapped token. * @param wrappedToken Address of the wrapped token that implements IERC4626 * @param amountUnderlyingRaw Amount of underlying tokens that will be deposited into the buffer * @param amountWrappedRaw Amount of wrapped tokens that will be deposited into the buffer * @param minIssuedShares Minimum amount of shares to receive from the buffer, expressed in underlying token * native decimals * @param sharesOwner Address that will own the deposited liquidity. Only this address will be able to remove * liquidity from the buffer * @return issuedShares the amount of tokens sharesOwner has in the buffer, expressed in underlying token amounts. * (it is the BPT of an internal ERC4626 buffer). It is expressed in underlying token native decimals. */ function initializeBuffer( IERC4626 wrappedToken, uint256 amountUnderlyingRaw, uint256 amountWrappedRaw, uint256 minIssuedShares, address sharesOwner ) external returns (uint256 issuedShares); /** * @notice Adds liquidity to an internal ERC4626 buffer in the Vault, proportionally. * @dev The buffer needs to be initialized beforehand. * @param wrappedToken Address of the wrapped token that implements IERC4626 * @param maxAmountUnderlyingInRaw Maximum amount of underlying tokens to add to the buffer. It is expressed in * underlying token native decimals * @param maxAmountWrappedInRaw Maximum amount of wrapped tokens to add to the buffer. It is expressed in wrapped * token native decimals * @param exactSharesToIssue The value in underlying tokens that `sharesOwner` wants to add to the buffer, * in underlying token decimals * @param sharesOwner Address that will own the deposited liquidity. Only this address will be able to remove * liquidity from the buffer * @return amountUnderlyingRaw Amount of underlying tokens deposited into the buffer * @return amountWrappedRaw Amount of wrapped tokens deposited into the buffer */ function addLiquidityToBuffer( IERC4626 wrappedToken, uint256 maxAmountUnderlyingInRaw, uint256 maxAmountWrappedInRaw, uint256 exactSharesToIssue, address sharesOwner ) external returns (uint256 amountUnderlyingRaw, uint256 amountWrappedRaw); /** * @notice Removes liquidity from an internal ERC4626 buffer in the Vault. * @dev Only proportional exits are supported, and the sender has to be the owner of the shares. * This function unlocks the Vault just for this operation; it does not work with a Router as an entrypoint. * * Pre-conditions: * - The buffer needs to be initialized. * - sharesOwner is the original msg.sender, it needs to be checked in the Router. That's why * this call is authenticated; only routers approved by the DAO can remove the liquidity of a buffer. * - The buffer needs to have some liquidity and have its asset registered in `_bufferAssets` storage. * * @param wrappedToken Address of the wrapped token that implements IERC4626 * @param sharesToRemove Amount of shares to remove from the buffer. Cannot be greater than sharesOwner's * total shares. It is expressed in underlying token native decimals * @param minAmountUnderlyingOutRaw Minimum amount of underlying tokens to receive from the buffer. It is expressed * in underlying token native decimals * @param minAmountWrappedOutRaw Minimum amount of wrapped tokens to receive from the buffer. It is expressed in * wrapped token native decimals * @return removedUnderlyingBalanceRaw Amount of underlying tokens returned to the user * @return removedWrappedBalanceRaw Amount of wrapped tokens returned to the user */ function removeLiquidityFromBuffer( IERC4626 wrappedToken, uint256 sharesToRemove, uint256 minAmountUnderlyingOutRaw, uint256 minAmountWrappedOutRaw ) external returns (uint256 removedUnderlyingBalanceRaw, uint256 removedWrappedBalanceRaw); /** * @notice Returns the asset registered for a given wrapped token. * @dev The asset can never change after buffer initialization. * @param wrappedToken Address of the wrapped token that implements IERC4626 * @return underlyingToken Address of the underlying token registered for the wrapper; `address(0)` if the buffer * has not been initialized. */ function getBufferAsset(IERC4626 wrappedToken) external view returns (address underlyingToken); /** * @notice Returns the shares (internal buffer BPT) of a liquidity owner: a user that deposited assets * in the buffer. * * @param wrappedToken Address of the wrapped token that implements IERC4626 * @param liquidityOwner Address of the user that owns liquidity in the wrapped token's buffer * @return ownerShares Amount of shares allocated to the liquidity owner, in native underlying token decimals */ function getBufferOwnerShares( IERC4626 wrappedToken, address liquidityOwner ) external view returns (uint256 ownerShares); /** * @notice Returns the supply shares (internal buffer BPT) of the ERC4626 buffer. * @param wrappedToken Address of the wrapped token that implements IERC4626 * @return bufferShares Amount of supply shares of the buffer, in native underlying token decimals */ function getBufferTotalShares(IERC4626 wrappedToken) external view returns (uint256 bufferShares); /** * @notice Returns the amount of underlying and wrapped tokens deposited in the internal buffer of the Vault. * @dev All values are in native token decimals of the wrapped or underlying tokens. * @param wrappedToken Address of the wrapped token that implements IERC4626 * @return underlyingBalanceRaw Amount of underlying tokens deposited into the buffer, in native token decimals * @return wrappedBalanceRaw Amount of wrapped tokens deposited into the buffer, in native token decimals */ function getBufferBalance( IERC4626 wrappedToken ) external view returns (uint256 underlyingBalanceRaw, uint256 wrappedBalanceRaw); /******************************************************************************* Authentication *******************************************************************************/ /** * @notice Sets a new Authorizer for the Vault. * @dev This is a permissioned call. Emits an `AuthorizerChanged` event. * @param newAuthorizer The address of the new authorizer */ function setAuthorizer(IAuthorizer newAuthorizer) external; }