BracketVaultV2

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/vaults/BracketVaultV2.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {IBracketVaultV2} from "./IBracketVaultV2.sol";
import {RebasingToken, IRebasingToken} from "./RebasingToken.sol";

import {AccessControlUpgradeable} from "openzeppelin-contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {PausableUpgradeable} from "openzeppelin-contracts-upgradeable/utils/PausableUpgradeable.sol";
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";

contract BracketVaultV2 is RebasingToken, AccessControlUpgradeable, PausableUpgradeable, IBracketVaultV2 {
    using SafeERC20 for IERC20Metadata;
    
    bytes32 public constant MANAGER_FEE_CLAIMER_ROLE = keccak256("MANAGER_FEE_CLAIMER_ROLE");
    bytes32 public constant BRACKET_FEE_CLAIMER_ROLE = keccak256("BRACKET_FEE_CLAIMER_ROLE");
    bytes32 public constant NAV_UPDATER_ROLE = keccak256("NAV_UPDATER_ROLE");
    bytes32 public constant ADMIN_LITE_ROLE = keccak256("ADMIN_LITE_ROLE");

    mapping(bytes32 => WithdrawalStatus) public withdrawals;

    mapping(address => Deposit) public lastDeposit;
    mapping(uint16 => uint256) public totalWithdrawals;
    mapping(uint16 => uint256) public navs;

    uint256 public vanityNav;
    uint256 public totalDeposits;
    uint256 public totalQueuedDeposits;

    int256 private totalPendingShares;

    uint256 public accruedManagerPerformanceFees;
    uint256 public accruedManagerTvlFees;
    uint256 public accruedBrktTvlFees;

    IERC20Metadata public token;
    address public manager;

    uint48 public lockFrequency;
    uint48 public nextLock;

    uint16 public epoch;
    uint16 public withdrawDelay;

    uint256 private _totalNonMintedShares;

    // ╔═══════════════════════════╗
    // ║ CONSTRUCTOR & INITIALIZER ║
    // ╚═══════════════════════════╝

    modifier onlyUnlocked() {
        if (isVaultLocked()) revert VaultLocked();
        _;
    }

    modifier onlyLocked() {
        if (!isVaultLocked()) revert VaultNotLocked();
        _;
    }

    constructor(address _kycWhitelist) RebasingToken(_kycWhitelist) {
        _disableInitializers();
    }

    function initialize(
        string memory name,
        string memory symbol,
        address _token,
        address _manager,
        address multisig,
        uint16 _withdrawDelay,
        uint48 _lockFrequency,
        address[] memory _accounts,
        bytes32[] memory _roles
    ) external initializer {
        if (_manager == address(0) || multisig == address(0) || _token == address(0)) revert ZeroAddress();

        uint8 _decimals = IERC20Metadata(_token).decimals();

        __AccessControl_init();
        __Pausable_init();
        __RebasingToken_init(name, symbol, _decimals);

        _grantRole(DEFAULT_ADMIN_ROLE, multisig);

        uint256 length = _accounts.length;
        if (length != _roles.length) revert InvalidRolesLength();
        for (uint256 i = 0; i < length; i++) {
            if (_accounts[i] == address(0)) revert ZeroAddress();
            if (_roles[i] == DEFAULT_ADMIN_ROLE) revert InvalidRole();
            _grantRole(_roles[i], _accounts[i]);
        }

        navs[0] = 10 ** _decimals;
        vanityNav = 10 ** _decimals;
        token = IERC20Metadata(_token);
        manager = _manager;

        _setLockFrequency(_lockFrequency);
        _setWithdrawDelay(_withdrawDelay);
    }

    // ╔═══════════════════════════╗
    // ║  EXTERNAL ADMIN FUNCTIONS ║
    // ╚═══════════════════════════╝

    function startVault(uint256 minimumDeposits) external onlyRole(NAV_UPDATER_ROLE) whenNotPaused {
        // Get the total deposits to save on gas
        uint256 _totalDeposits = totalDeposits;

        if (epoch != 0) revert VaultAlreadyStarted();
        if (_totalDeposits < minimumDeposits) revert MinimumDepositsNotReached();

        // Transfer all the assets to the manager
        token.safeTransfer(manager, _totalDeposits);

        // Account for the pending shares, with a 1:1 NAV
        totalPendingShares = int256(_totalDeposits);
        _totalNonMintedShares = _totalDeposits;

        // Reset the total deposits
        totalDeposits = 0;
        epoch = 1;

        nextLock = uint48(block.timestamp) + lockFrequency;

        // Emit the event
        emit VaultStarted(_totalDeposits);
    }

    function updateNav(uint256 newNav, uint256 managerPerformanceFee, uint256 managerTvlFee, uint256 brktTvlFee) external onlyRole(NAV_UPDATER_ROLE) onlyLocked whenNotPaused {
        if (epoch == 0) revert VaultNotStarted();

        nextLock += lockFrequency;

        // Add to the accrued fees
        accruedManagerPerformanceFees += managerPerformanceFee;
        accruedManagerTvlFees += managerTvlFee;
        accruedBrktTvlFees += brktTvlFee;

        uint256 totalFees = managerPerformanceFee + managerTvlFee + brktTvlFee;

        // Process deposits and withdrawals
        (uint256 clearedDepositAssets, uint256 clearedWithdrawalAssets) = _processDepositsWithdrawals(newNav, totalFees);

        // Set the new NAV and move to the next epoch
        navs[epoch] = newNav;
        vanityNav = newNav;

        // Update the epoch
        epoch++;

        // Emit the event
        emit NavUpdated(epoch - 1, newNav, clearedDepositAssets, clearedWithdrawalAssets, managerPerformanceFee, managerTvlFee, brktTvlFee);
    }

    function claimManagerFees() external onlyRole(MANAGER_FEE_CLAIMER_ROLE) whenNotPaused {
        // Transfer the fees from the vault to the claimer
        uint256 totalFees = accruedManagerPerformanceFees + accruedManagerTvlFees;
        if (totalFees == 0) revert NoFeesToClaim();
        
        token.safeTransfer(msg.sender, totalFees);

        // Reset the accrued fees
        accruedManagerPerformanceFees = 0;
        accruedManagerTvlFees = 0;
    }

    function claimBrktTvlFees() external onlyRole(BRACKET_FEE_CLAIMER_ROLE) whenNotPaused {
        // Transfer the fees from the manager to the claimer
        token.safeTransfer(msg.sender, accruedBrktTvlFees);

        // Reset the accrued fees
        accruedBrktTvlFees = 0;
    }

    function setManager(address _manager) external onlyRole(ADMIN_LITE_ROLE) whenNotPaused {
        if (_manager == address(0)) revert ZeroAddress();

        manager = _manager;
    }

    function setNextLock(uint48 _nextLock) external onlyRole(ADMIN_LITE_ROLE) whenNotPaused {
        if (_nextLock <= block.timestamp) revert InvalidLockTime();
        nextLock = _nextLock;
    }

    function setLockFrequency(uint48 _lockFrequency) external onlyRole(ADMIN_LITE_ROLE) whenNotPaused {
        _setLockFrequency(_lockFrequency);
    }

    function setWithdrawDelay(uint16 _withdrawDelay) external onlyRole(ADMIN_LITE_ROLE) whenNotPaused {
        _setWithdrawDelay(_withdrawDelay);
    }

    // This function is used to fix the total non minted shares after an upgrade, will be batched along with the upgrade
    function fixTotalNonMintedShares() external onlyRole(ADMIN_LITE_ROLE) whenNotPaused {
        require(_totalNonMintedShares == 0, "ALREADY FIXED");
        require(totalPendingShares >= 0, "TOTAL PENDING SHARES IS NEGATIVE");

        uint256 sumWithdrawals = 0;
        uint16 currentEpoch = epoch;
        uint16 maxEpoch = currentEpoch + withdrawDelay;

        for(uint16 i = currentEpoch; i <= maxEpoch; i++) {
            sumWithdrawals += totalWithdrawals[i];
        }

        _totalNonMintedShares = uint256(totalPendingShares) - sumWithdrawals;
    }

    function switchPause() external onlyRole(ADMIN_LITE_ROLE) {
        if (paused()) {
            _unpause();
        } else {
            _pause();
        }
    }

    function setNameAndSymbol(string memory name_, string memory symbol_) external onlyRole(ADMIN_LITE_ROLE) whenNotPaused {
        ERC20Storage storage $;
        assembly {
            $.slot := 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00
        }
        $._name = name_;
        $._symbol = symbol_;
    }

    // ╔═══════════════════════════╗
    // ║  EXTERNAL USER FUNCTIONS  ║
    // ╚═══════════════════════════╝

    function deposit(uint256 assets, address destination) external whenNotPaused {
        if (assets == 0) revert ZeroAmount();

        _checkDepositKYC();

        // Transfer the assets to the vault
        token.safeTransferFrom(msg.sender, address(this), assets);

        // Try to clear any pending deposit from past epochs
        _clearDeposit(destination);

        uint16 _epoch = epoch;
        lastDeposit[destination].epoch = _epoch;
        if (isVaultLocked()) {
            // If the vault is locked, queue the deposit
            lastDeposit[destination].queuedAssets += assets;

            // Update the total queued deposits
            totalQueuedDeposits += assets;

            emit PendingDeposit(destination, assets, _epoch + 2);
        } else { 
            // If the vault is unlocked, add the asset to the last deposit assets
            lastDeposit[destination].assets += assets;

            // Update the total deposits
            totalDeposits += assets;

            emit PendingDeposit(destination, assets, _epoch + 1);
        }
    }

    function withdraw(uint256 assets, bytes32 salt) external onlyUnlocked whenNotPaused {
        if (assets == 0) revert ZeroAmount();

        _checkWithdrawalKYC();

        // Get the current epoch to memory to save on gas and calculate the claim epoch
        uint16 currentEpoch = epoch;

        // If the vault is not started, allow users to withdraw instantly
        if (currentEpoch == 0) {
            _withdrawEpoch0(msg.sender, assets);
            return;
        }

        // Calculate the shares to withdraw
        uint256 shares = convertToShares(assets, navs[currentEpoch - 1]);
        if (shares == 0) revert ZeroAmount();

        uint16 claimEpoch = currentEpoch + withdrawDelay;

        // Try to clear any pending deposit from past epochs
        _clearDeposit(msg.sender);

        // Burn the amount of receipt shares the user wants to withdraw, and add the shares to the total pending shares
        totalPendingShares += int256(shares);
        _burn(msg.sender, shares);

        // Add the withdrawal to the total withdrawals
        totalWithdrawals[claimEpoch - 1] += shares;

        // Schedule the withdrawal
        _scheduleWithdrawal(shares, claimEpoch, salt);
    }

    function claimWithdrawal(uint256 shares, uint16 claimEpoch, uint256 timestamp, bytes32 salt) external whenNotPaused {
        // Hash the withdrawal and check that it is ready
        (WithdrawalStatus status, bytes32 digest) = getWithdrawalStatusAndHash(msg.sender, shares, claimEpoch, timestamp, salt);
        if (status != WithdrawalStatus.Ready) revert WithdrawalNotReady();

        _checkWithdrawalKYC();

        // Calculate the amount of assets to send to the user
        uint256 assets = convertToAssets(shares, navs[claimEpoch - 1]);

        // Full withdrawal, set the withdrawal status to completed
        withdrawals[digest] = WithdrawalStatus.Completed;

        // Transfer the full amount of assets to the user
        token.safeTransfer(msg.sender, assets);

        // Emit the event
        emit ClaimedWithdrawal(msg.sender, digest, shares, assets, 0);
    }

    // ╔════════════════════════════════════╗
    // ║   PUBLIC VIEW AND PURE FUNCTIONS   ║
    // ╚════════════════════════════════════╝

    function isVaultLocked() public view returns (bool) {
        return block.timestamp > nextLock && epoch != 0;
    }

    function getWithdrawalStatusAndHash(address user, uint256 amount, uint16 claimEpoch, uint256 timestamp, bytes32 salt)
    public
    view
    returns (WithdrawalStatus, bytes32) {
        bytes32 digest = hashWithdrawal(user, claimEpoch, amount, timestamp, salt);
        WithdrawalStatus status = withdrawals[digest];

        if (status == WithdrawalStatus.Queued && epoch >= claimEpoch) {
            return (WithdrawalStatus.Ready, digest);
        }

        return (status, digest);
    }

    
    function hashWithdrawal(address user, uint16 claimEpoch, uint256 shares, uint256 timestamp, bytes32 salt) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(user, claimEpoch, shares, timestamp, salt));
    }

    // ---- RebasingToken functions ----
    function pendingMintShares(address account) public view override(IRebasingToken, RebasingToken) returns (uint256) {
        Deposit memory _lastDeposit = lastDeposit[account];

        // If in the current epoch
        if (_lastDeposit.epoch == epoch) {
            // We don't know any NAV, so shares can't be accounted for
            return 0;
        }

        // If in the previous epoch
        if (_lastDeposit.epoch + 1 == epoch) {
            // We know the NAV for the previous epoch, so we can calculate the shares of the assets in the previous epoch but not the queued assets
            return convertToShares(_lastDeposit.assets, navs[_lastDeposit.epoch]);
        }

        // If at least 2 epochs ago, we can calculate the shares of both the assets in the epoch and the queued assets in the epoch after
        return convertToShares(_lastDeposit.assets, navs[_lastDeposit.epoch]) + convertToShares(_lastDeposit.queuedAssets, navs[_lastDeposit.epoch + 1]);
    }

    function inactiveBalanceOf(address account) public view override(IRebasingToken, RebasingToken) returns (uint256) {
        Deposit memory _lastDeposit = lastDeposit[account];

        // If at least 2 epochs ago, no assets are inactive
        if (_lastDeposit.epoch + 1 < epoch) {
            return 0;
        }

        // If in the previous epoch
        if (_lastDeposit.epoch < epoch) {
            // We know the previous NAV, so only the queued assets are inactive
            return _lastDeposit.queuedAssets;
        }

        // If in the current epoch, both the assets in the epoch and the queued assets in the epoch after are inactive
        return _lastDeposit.assets + _lastDeposit.queuedAssets;
    }

    function totalNonMintedShares() public view override(IRebasingToken, RebasingToken) returns (uint256) {
        return _totalNonMintedShares;
    }

    function inactiveSupply() public view override(IRebasingToken, RebasingToken) returns (uint256) {
        return totalDeposits + totalQueuedDeposits;
    }

    function assetsPerShare() public view override(IRebasingToken, RebasingToken) returns (uint256) {
        if (epoch == 0) {
            return 10 ** decimals();
        } else {
            return navs[epoch - 1];
        }
    }

    // ╔═══════════════════════╗
    // ║   INTERNAL FUNCTIONS  ║
    // ╚═══════════════════════╝

    function _setLockFrequency(uint48 _lockFrequency) internal {
        if (_lockFrequency < 1 hours || _lockFrequency > 30 days) revert InvalidLockFrequency();
        lockFrequency = _lockFrequency;
    }

    function _setWithdrawDelay(uint16 _withdrawDelay) internal {
        if (_withdrawDelay == 0 || _withdrawDelay > 30) revert InvalidWithdrawalDelay();
        withdrawDelay = _withdrawDelay;
    }

    function _clearDeposit(address user) internal override {
        Deposit memory _lastDeposit = lastDeposit[user];
        uint16 _epoch = epoch;

        // If the last deposit is the current epoch, we can't clear it yet as we don't have the NAV
        if (_lastDeposit.epoch == _epoch) return;
        
        // If both the assets and the queued assets are 0, we don't need to mint but can consider it cleared
        if (_lastDeposit.assets == 0 && _lastDeposit.queuedAssets == 0) return;

        uint256 shares;

        // If the epoch is the previous epoch, we can clear the assets and move the queued assets to the queue
        if (_lastDeposit.epoch + 1 == _epoch) {
            shares = convertToShares(_lastDeposit.assets, navs[_lastDeposit.epoch]);

            // Update the last deposit by moving the queued assets to the assets and updating the epoch
            Deposit memory newLastDeposit = Deposit({
                epoch: _epoch,
                assets: _lastDeposit.queuedAssets,
                queuedAssets: 0
            });

            lastDeposit[user] = newLastDeposit;
        } else {
            // If the epoch is at least two epochs ago, we can clear both assets and queued assets
            shares = convertToShares(_lastDeposit.assets, navs[_lastDeposit.epoch]) + convertToShares(_lastDeposit.queuedAssets, navs[_lastDeposit.epoch + 1]);

            // Remove the last deposit for the user
            delete lastDeposit[user];
        }

        _mint(user, shares);

        // Remove the shares from the total pending shares as they are now part of the total supply
        totalPendingShares -= int256(shares);
        _totalNonMintedShares -= shares;
    }
    
    function _withdrawEpoch0(address user, uint256 assets) internal {
        Deposit memory _lastDeposit = lastDeposit[user];
        if (assets > _lastDeposit.assets) revert InsufficientBalance();

        // Deduct the assets from the user's deposit and total deposits
        lastDeposit[user].assets = _lastDeposit.assets - assets;
        totalDeposits -= assets;

        token.safeTransfer(user, assets);

        emit ClaimedWithdrawal(user, bytes32(uint256(0)), assets, assets, 0);
    }

    function _scheduleWithdrawal(uint256 shares, uint16 claimEpoch, bytes32 salt) internal {
        // Hash the withdrawal
        bytes32 digest = hashWithdrawal(msg.sender, claimEpoch, shares, block.timestamp, salt);

        // Check that there is no collision, eg: another user withdrawal with the same amount in the same tx with the same salt
        if (withdrawals[digest] != WithdrawalStatus.Unset) revert WithdrawalAlreadyQueued();

        // Set the withdrawal status to queued
        withdrawals[digest] = WithdrawalStatus.Queued;

        // Emit the event
        emit ScheduledWithdrawal(msg.sender, shares, claimEpoch, block.timestamp, salt);
    }

    function _processDepositsWithdrawals(uint256 nav, uint256 totalFees) internal returns(uint256, uint256) {
        // Calculate total deposits and withdrawals in assets
        uint256 depositAssets = totalDeposits;  
        uint256 depositShares = convertToShares(depositAssets, nav);
        uint256 withdrawalShares = totalWithdrawals[epoch];
        uint256 withdrawalAssets = convertToAssets(withdrawalShares, nav);
        uint256 totalDebit = withdrawalAssets + totalFees;
        
        // Account for the pending shares, add the shares deposited on this epoch and substract the shares that can be now claimed
        totalPendingShares += int256(depositShares) - int256(withdrawalShares);
        _totalNonMintedShares += depositShares;

        // Move the queued deposits to the total deposits
        totalDeposits = totalQueuedDeposits;

        // Clear the remaining totals for next epoch
        delete totalQueuedDeposits;
        delete totalWithdrawals[epoch];

        // Case 1: More deposits than withdrawals and fees
        if (depositAssets > totalDebit) {
            uint256 credit = depositAssets - totalDebit;
            // Send excess deposits to manager
            token.safeTransfer(manager, credit);
            return (depositAssets, withdrawalAssets);
        } 
        // Case 2: More withdrawals and fees than deposits
        else if (depositAssets < totalDebit) {
            uint256 debit = totalDebit - depositAssets;
            uint256 available = _getManagerAvailableBalance();

            if (available < debit) revert InsufficientManagerFunds();

            // Transfer the available balance to the vault
            token.safeTransferFrom(manager, address(this), debit);

            return (depositAssets, withdrawalAssets);
        }
        // Case 3: Deposits equal withdrawals and fees - no transfer needed
        return (depositAssets, withdrawalAssets);
    }

    function _getManagerAvailableBalance() internal view returns (uint256) {
        uint256 balance = token.balanceOf(manager);
        uint256 allowance = token.allowance(manager, address(this));
        
        // Get the minimum of the balance or the allowance
        return Math.min(balance, allowance);
    }

    function _checkDepositKYC() internal view {
        if (!KYC_WHITELIST.canDeposit(address(this), msg.sender)) revert KYCFailed();
    }

    function _checkWithdrawalKYC() internal view {
        if (!KYC_WHITELIST.canWithdraw(address(this), msg.sender)) revert KYCFailed();
    }
}"
    },
    "src/vaults/IBracketVaultV2.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IRebasingToken} from "./IRebasingToken.sol";

/**
 * @title IBracketVault
 * @notice Interface for the BracketVault contract which manages deposits, withdrawals, and NAV updates
 */
interface IBracketVaultV2 is IRebasingToken {
    error KYCFailed();
    error InsufficientBalance();
    error InsufficientManagerFunds();
    error InvalidWithdrawalDelay();
    error InvalidLockFrequency();
    error InvalidLength();
    error InvalidLockTime();
    error InvalidRole();
    error InvalidRolesLength();
    error MinimumDepositsNotReached();
    error NoFeesToClaim();
    error VaultAlreadyStarted();
    error VaultNotStarted();
    error VaultLocked();
    error VaultNotLocked();
    error WithdrawalAlreadyQueued();
    error WithdrawalNotReady();
    error ZeroAmount();

    /**
     * @notice Represents the status of a withdrawal request
     * @param Unset Initial state, withdrawal has not been requested
     * @param Queued Withdrawal has been requested but not yet ready for claiming
     * @param Ready Withdrawal is ready to be claimed
     * @param Partial Withdrawal was partially processed due to insufficient funds
     * @param Completed Withdrawal has been fully processed and claimed
     */
    enum WithdrawalStatus {
        Unset,
        Queued,
        Ready,
        Partial,
        Completed
    }

    /**
     * @notice Structure representing a user's deposit
     * @param epoch The epoch when the deposit was made
     * @param assets The amount of assets deposited
     * @param queuedAssets The amount of assets queued for withdrawal
     */
    struct Deposit {
        uint16 epoch;
        uint256 assets;
        uint256 queuedAssets;
    }

    /**
     * @notice Emitted when a deposit happens
     * @param user Address of the user making the deposit
     * @param assets Amount of assets to deposit
     * @param epoch Epoch when the deposit will be credited
     */
    event PendingDeposit(address indexed user, uint256 assets, uint16 epoch);

    /**
     * @notice Emitted when a withdrawal is scheduled
     * @param user Address of the user scheduling the withdrawal
     * @param shares Amount of shares to withdraw
     * @param claimEpoch Epoch when the withdrawal can be claimed
     * @param timestamp Time when the withdrawal was scheduled
     * @param salt Random value to prevent withdrawal hash collisions
     */
    event ScheduledWithdrawal(address indexed user, uint256 shares, uint16 claimEpoch, uint256 timestamp, bytes32 salt);

    /**
     * @notice Emitted when a withdrawal is claimed
     * @param user Address of the user claiming the withdrawal
     * @param withdrawalId Unique identifier of the withdrawal
     * @param shares Amount of shares withdrawn
     * @param assets Amount of assets received
     * @param remainingAssets Amount of assets still owed if partial withdrawal
     */
    event ClaimedWithdrawal(
        address indexed user, bytes32 withdrawalId, uint256 shares, uint256 assets, uint256 remainingAssets
    );

    /**
     * @notice Emitted when NAV is updated
     * @param epoch Current epoch number
     * @param nav New NAV value
     * @param clearedDeposits Total deposits processed
     * @param clearedWithdrawals Total withdrawals processed
     * @param managerPerformanceFee Performance fee for the manager
     * @param managerTvlFee TVL-based fee for the manager
     * @param brktTvlFee TVL-based fee for BRKT
     */
    event NavUpdated(
        uint256 indexed epoch,
        uint256 nav,
        uint256 clearedDeposits,
        uint256 clearedWithdrawals,
        uint256 managerPerformanceFee,
        uint256 managerTvlFee,
        uint256 brktTvlFee
    );

    /**
     * @notice Emitted when vanity NAV is updated
     * @param epoch Current epoch number
     * @param newNav New vanity NAV value
     */
    event VanityNavUpdated(uint256 indexed epoch, uint256 newNav);

    /**
     * @notice Emitted when vault is started
     * @param totalDeposits Total initial deposits
     */
    event VaultStarted(uint256 totalDeposits);

    /**
     * @notice Starts the vault, transferring initial deposits to the manager
     * @param minimumDeposits Minimum number of deposits required to start the vault
     * @dev Can only be called by NAV_UPDATER_ROLE
     */
    function startVault(uint256 minimumDeposits) external;

    /**
     * @notice Updates the NAV and processes deposits/withdrawals
     * @param newNav New NAV value
     * @param managerPerformanceFee Performance fee for the manager
     * @param managerTvlFee TVL-based fee for the manager
     * @param brktTvlFee TVL-based fee for BRKT
     */
    function updateNav(uint256 newNav, uint256 managerPerformanceFee, uint256 managerTvlFee, uint256 brktTvlFee) external;

    /**
     * @notice Claims accrued manager fees
     */
    function claimManagerFees() external;

    /**
     * @notice Claims accrued BRKT TVL fees
     */
    function claimBrktTvlFees() external;

    /**
     * @notice Deposits assets into the vault
     * @param assets Amount of assets to deposit
     * @param destination Address to send the deposit to
     */
    function deposit(uint256 assets, address destination) external;

    /**
     * @notice Initiates a withdrawal request
     * @param assets Amount of assets to withdraw
     * @param salt Random value to prevent withdrawal hash collisions
     */
    function withdraw(uint256 assets, bytes32 salt) external;

    /**
     * @notice Claims a previously scheduled withdrawal
     * @param shares Amount of shares to withdraw
     * @param claimEpoch Epoch when the withdrawal can be claimed
     * @param timestamp Time when the withdrawal was scheduled
     * @param salt Random value used in the withdrawal request
     */
    function claimWithdrawal(uint256 shares, uint16 claimEpoch, uint256 timestamp, bytes32 salt) external;

    /**
     * @notice Returns the status and hash of a withdrawal request
     * @param user Address of the user
     * @param amount Amount of shares to withdraw
     * @param claimEpoch Epoch when the withdrawal can be claimed
     * @param timestamp Time when the withdrawal was scheduled
     * @param salt Random value used in the withdrawal request
     * @return WithdrawalStatus status of the withdrawal
     * @return bytes32 hash of the withdrawal
     */
    function getWithdrawalStatusAndHash(address user, uint256 amount, uint16 claimEpoch, uint256 timestamp, bytes32 salt)
        external
        view
        returns (WithdrawalStatus, bytes32);

    // /**
    //  * @notice Computes the hash of a withdrawal request
    //  * @param user Address of the user
    //  * @param claimEpoch Epoch when the withdrawal can be claimed
    //  * @param shares Amount of shares to withdraw
    //  * @param timestamp Time when the withdrawal was scheduled
    //  * @param salt Random value to prevent hash collisions
    //  * @return Hash of the withdrawal request
    //  */
    // function hashWithdrawal(address user, uint16 claimEpoch, uint256 shares, uint256 timestamp, bytes32 salt)
    //     internal
    //     pure
    //     returns (bytes32);

    /**
     * @notice epoch accessor
     */
    function epoch() external view returns (uint16);
    /**
     * @notice withdrawDelay accessor
     */
    function withdrawDelay() external view returns (uint16);
}
"
    },
    "src/vaults/RebasingToken.sol": {
      "content": "pragma solidity ^0.8.20;

import {IRebasingToken, IERC20Metadata} from "./IRebasingToken.sol";

import {IERC20, ERC20Upgradeable} from "openzeppelin-contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";
import {KYCWhitelist} from "./KYCWhitelist.sol";

/**
 * @title RebasingToken
 * @notice Abstract contract for rebasing tokens that automatically adjust balances based on an assets-per-share ratio
 * @dev Implements IRebasingToken interface
 */
abstract contract RebasingToken is ERC20Upgradeable, IRebasingToken {
    error TransferNotAllowed();
    error ZeroAddress();
    
    KYCWhitelist public immutable KYC_WHITELIST;

    uint8 private _decimals;

    constructor(address kycWhitelist) {
        if (kycWhitelist == address(0)) revert ZeroAddress();
        KYC_WHITELIST = KYCWhitelist(kycWhitelist);
    }

    function __RebasingToken_init(string memory name, string memory symbol, uint8 tokenDecimals) internal onlyInitializing {
        __ERC20_init(name, symbol);
        _decimals = tokenDecimals;
    }

    function mintedSharesOf(address account) public view returns (uint256) {
        return super.balanceOf(account);
    }

    function pendingMintShares(address account) public view virtual returns (uint256);

    function sharesOf(address account) public view returns (uint256) {
        return mintedSharesOf(account) + pendingMintShares(account);
    }

    function totalMintedShares() public view returns (uint256) {
        return super.totalSupply();
    }

    function totalNonMintedShares() public view virtual returns (uint256);

    function totalShares() public view returns (uint256) {
        return totalMintedShares() + totalNonMintedShares();
    }
    
    function activeBalanceOf(address account) public view returns (uint256) {
        return convertToAssets(sharesOf(account), assetsPerShare());
    }

    function inactiveBalanceOf(address account) public view virtual returns (uint256);

    function balanceOf(address account) public view override(IERC20, ERC20Upgradeable) returns (uint256) {
        return activeBalanceOf(account);
    }

    function activeSupply() public view returns (uint256) {
        return convertToAssets(totalShares(), assetsPerShare());
    }

    function inactiveSupply() public view virtual returns (uint256);

    function totalSupply() public view override(IERC20, ERC20Upgradeable) returns (uint256) {
        return activeSupply();
    }

    function convertToShares(uint256 assets, uint256 _assetsPerShare) public view virtual returns (uint256) {
        return Math.mulDiv(assets, 10 ** _decimals, _assetsPerShare);
    }

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        return convertToShares(assets, assetsPerShare());
    }

    function convertToAssets(uint256 shares, uint256 _assetsPerShare) public view virtual returns (uint256) {
        return Math.mulDiv(shares, _assetsPerShare, 10 ** _decimals);
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        return convertToAssets(shares, assetsPerShare());
    }

    function transfer(address to, uint256 value) public virtual override (IERC20, ERC20Upgradeable) returns (bool) {
        uint256 shares = convertToShares(value, assetsPerShare());

        return super.transfer(to, shares);
    }

    function transferFrom(address from, address to, uint256 value) public virtual override (IERC20, ERC20Upgradeable) returns (bool) {
        uint256 shares = convertToShares(value, assetsPerShare());

        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, shares);
        return true;
    }

    function assetsPerShare() public view virtual returns (uint256);

    function _clearDeposit(address user) internal virtual;

    function _update(address from, address to, uint256 value) internal override(ERC20Upgradeable) {
        if (!KYC_WHITELIST.canTransfer(address(this), from, to)) revert TransferNotAllowed();

        if (from != address(0) && to != address(0)) {
            // If it's not a mint or burn, clear the deposits
            _clearDeposit(from);
        }

        super._update(from, to, value);
    }

    function decimals() public view override(IERC20Metadata, ERC20Upgradeable) returns (uint8) {
        return _decimals;
    }
}"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;


    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
    struct AccessControlStorage {
        mapping(bytes32 role => RoleData) _roles;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;

    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
        assembly {
            $.slot := AccessControlStorageLocation
        }
    }

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        AccessControlStorage storage $ = _getAccessControlStorage();
        bytes32 previousAdminRole = getRoleAdmin(role);
        $._roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (!hasRole(role, account)) {
            $._roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (hasRole(role, account)) {
            $._roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Pausable
    struct PausableStorage {
        bool _paused;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;

    function _getPausableStorage() private pure returns (PausableStorage storage $) {
        assembly {
            $.slot := PausableStorageLocation
        }
    }

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        PausableStorage storage $ = _getPausableStorage();
        return $._paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
        emit Unpaused(_msgSender());
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/math/Math.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
          

Tags:
ERC20, ERC165, Multisig, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xf29de913ffeccd43ac4b5b84b734a0438afb6e9c|verified:true|block:23391863|tx:0x80e53a231b2897a21dbd71e7678439f42a24ad2f02db0a96a2ee8afe3f9422af|first_check:1758302012

Submitted on: 2025-09-19 19:13:33

Comments

Log in to comment.

No comments yet.