ETH Price: $3,273.70 (+0.72%)

Contract

0xC570c434ba30a2fa5C07E590833246E18aa6B0a3
 
Transaction Hash
Method
Block
From
To
Log Claim213709022024-12-10 8:26:2331 days ago1733819183IN
0xC570c434...18aa6B0a3
0 ETH0.0003573613.1024131
Log Claim210148582024-10-21 15:29:3581 days ago1729524575IN
0xC570c434...18aa6B0a3
0 ETH0.0003922914.38293372
Log Claim209845052024-10-17 9:51:1185 days ago1729158671IN
0xC570c434...18aa6B0a3
0 ETH0.000423715.53449844
Log Claim209662402024-10-14 20:38:1187 days ago1728938291IN
0xC570c434...18aa6B0a3
0 ETH0.000431515.82037506
Log Claim209052332024-10-06 8:11:1196 days ago1728202271IN
0xC570c434...18aa6B0a3
0 ETH0.000152955.60774724
Log Claim208983752024-10-05 9:15:5997 days ago1728119759IN
0xC570c434...18aa6B0a3
0 ETH0.000123594.53149854
Log Claim208915472024-10-04 10:25:4798 days ago1728037547IN
0xC570c434...18aa6B0a3
0 ETH0.00012914.73338465
Log Claim207764422024-09-18 8:59:47114 days ago1726649987IN
0xC570c434...18aa6B0a3
0 ETH0.000198987.29561656
Log Claim207442912024-09-13 21:10:59118 days ago1726261859IN
0xC570c434...18aa6B0a3
0 ETH0.000157095.7596745
Log Claim205792392024-08-21 19:59:47141 days ago1724270387IN
0xC570c434...18aa6B0a3
0 ETH0.00007392.70947311
Log Claim205224522024-08-13 21:40:47149 days ago1723585247IN
0xC570c434...18aa6B0a3
0 ETH0.000083193.05006863
Log Claim203894122024-07-26 8:04:47168 days ago1721981087IN
0xC570c434...18aa6B0a3
0 ETH0.000057092.0931257
Log Claim203866382024-07-25 22:46:59168 days ago1721947619IN
0xC570c434...18aa6B0a3
0 ETH0.00006742.47146003
Log Claim203817552024-07-25 6:24:47169 days ago1721888687IN
0xC570c434...18aa6B0a3
0 ETH0.000104763.84107462
Log Claim203726512024-07-23 23:55:11170 days ago1721778911IN
0xC570c434...18aa6B0a3
0 ETH0.000065112.38734681
Log Claim203507632024-07-20 22:35:35173 days ago1721514935IN
0xC570c434...18aa6B0a3
0 ETH0.000084713.10595325
Log Claim203059542024-07-14 16:31:23180 days ago1720974683IN
0xC570c434...18aa6B0a3
0 ETH0.000133914.90992415
Log Claim202266632024-07-03 14:45:11191 days ago1720017911IN
0xC570c434...18aa6B0a3
0 ETH0.0003714513.61901999
Log Claim202185932024-07-02 11:40:11192 days ago1719920411IN
0xC570c434...18aa6B0a3
0 ETH0.000147535.40932999
Log Claim202177242024-07-02 8:45:47192 days ago1719909947IN
0xC570c434...18aa6B0a3
0 ETH0.000106943.92113468
Log Claim202112762024-07-01 11:09:23193 days ago1719832163IN
0xC570c434...18aa6B0a3
0 ETH0.000160615.88873348
Log Claim201968442024-06-29 10:47:47195 days ago1719658067IN
0xC570c434...18aa6B0a3
0 ETH0.000056582.07451213
Log Claim201912922024-06-28 16:10:35196 days ago1719591035IN
0xC570c434...18aa6B0a3
0 ETH0.000193657.1
Log Claim201429992024-06-21 22:13:11202 days ago1719007991IN
0xC570c434...18aa6B0a3
0 ETH0.000105283.86009595
Log Claim201408252024-06-21 14:54:35203 days ago1718981675IN
0xC570c434...18aa6B0a3
0 ETH0.000256749.41315928
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
197190962024-04-23 15:50:35262 days ago1713887435
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
192638832024-02-19 19:34:11325 days ago1708371251
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
192347472024-02-15 17:13:47330 days ago1708017227
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
191775472024-02-07 16:36:47338 days ago1707323807
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
191687972024-02-06 11:06:23339 days ago1707217583
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
191612752024-02-05 9:46:59340 days ago1707126419
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
191347242024-02-01 16:19:35344 days ago1706804375
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
190274592024-01-17 15:20:11359 days ago1705504811
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
189435522024-01-05 20:51:11370 days ago1704487871
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
186832532023-11-30 8:32:23407 days ago1701333143
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
185765122023-11-15 9:50:59422 days ago1700041859
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
185043382023-11-05 7:31:35432 days ago1699169495
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
184786382023-11-01 17:06:47436 days ago1698858407
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
184353272023-10-26 15:37:35442 days ago1698334655
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
184206252023-10-24 14:10:35444 days ago1698156635
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
184192752023-10-24 9:38:11444 days ago1698140291
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
183901102023-10-20 7:40:35448 days ago1697787635
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
183773232023-10-18 12:45:47450 days ago1697633147
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
182716802023-10-03 18:06:47464 days ago1696356407
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
182692982023-10-03 10:04:23465 days ago1696327463
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
182202302023-09-26 13:24:35472 days ago1695734675
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
182124672023-09-25 11:21:11473 days ago1695640871
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
180480992023-09-02 10:00:59496 days ago1693648859
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
179618962023-08-21 8:24:59508 days ago1692606299
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
179435642023-08-18 18:49:23510 days ago1692384563
0xC570c434...18aa6B0a3
 Contract Creation0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
HATVaultsRegistry

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 29 : HATVaultsRegistry.sol
// SPDX-License-Identifier: MIT
// Disclaimer https://github.com/hats-finance/hats-contracts/blob/main/DISCLAIMER.md

pragma solidity 0.8.16;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "./tokenlock/TokenLockFactory.sol";
import "./interfaces/IHATVaultsRegistry.sol";
import "./HATVault.sol";

/** @title Registry to deploy Hats.finance vaults and manage shared parameters
 * @author Hats.finance
 * @notice Hats.finance is a proactive bounty protocol for white hat hackers and
 * security experts, where projects, community members, and stakeholders
 * incentivize protocol security and responsible disclosure.
 * Hats create scalable vaults using the project’s own token. The value of the
 * bounty increases with the success of the token and project.
 *
 * The owner of the registry has the permission to set time limits and bounty
 * parameters and change vaults' info, and to set the other registry roles -
 * fee setter and arbitrator.
 * The arbitrator can challenge submitted claims for bounty payouts made by
 * vaults' committees, approve them with a different bounty percentage or
 * dismiss them.
 * The fee setter can set the fee on withdrawals on all vaults.
 *
 * This project is open-source and can be found at:
 * https://github.com/hats-finance/hats-contracts
 *
 * @dev New hats.finance vaults should be created through a call to {createVault}
 * so that they are linked to the registry
 */
contract HATVaultsRegistry is IHATVaultsRegistry, Ownable {
    using SafeERC20 for IERC20;
    using Math for uint256;

    // Used in {swapAndSend} to avoid a "stack too deep" error
    struct SwapData {
        uint256 amount;
        uint256 amountUnused;
        uint256 hatsReceived;
        uint256 totalHackerReward;
        uint256 governanceAmountSwapped;
        uint256[] hackerRewards;
        uint256 governanceHatReward;
        uint256 usedPart;
    }

    uint16 public constant HUNDRED_PERCENT = 10000;
    // the maximum percentage of the bounty that will be converted in HATs
    uint16 public constant MAX_HAT_SPLIT = 2000;

    address public immutable hatVaultImplementation;
    address[] public hatVaults;
    
    // vault address => is visible
    mapping(address => bool) public isVaultVisible;
    // asset => hacker address => amount
    mapping(address => mapping(address => uint256)) public hackersHatReward;
    // asset => amount
    mapping(address => uint256) public governanceHatReward;

    // PARAMETERS FOR ALL VAULTS
    IHATVaultsRegistry.GeneralParameters public generalParameters;
    ITokenLockFactory public immutable tokenLockFactory;

    // the token into which a part of the the bounty will be swapped into
    IERC20 public HAT;
    
    // feeSetter sets the withdrawal fee
    address public feeSetter;

    // How the bountyGovernanceHAT and bountyHackerHATVested set how to divide the hats 
    // bounties of the vault, in percentages (out of `HUNDRED_PERCENT`)
    // The precentages are taken from the total bounty
 
    // the default percentage of the total bounty to be swapped to HATs and sent to governance
    uint16 public defaultBountyGovernanceHAT;
    // the default percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract
    uint16 public defaultBountyHackerHATVested;

    address public defaultArbitrator;
    bool public defaultArbitratorCanChangeBounty;

    bool public isEmergencyPaused;
    uint32 public defaultChallengePeriod;
    uint32 public defaultChallengeTimeOutPeriod;

    /**
    * @notice initialize -
    * @param _hatVaultImplementation The hat vault implementation address.
    * @param _hatGovernance The governance address.
    * @param _HAT the HAT token address
    * @param _bountyGovernanceHAT The default percentage of a claim's total
    * bounty to be swapped for HAT and sent to the governance
    * @param _bountyHackerHATVested The default percentage of a claim's total
    * bounty to be swapped for HAT and sent to a vesting contract for the hacker
    *   _bountyGovernanceHAT + _bountyHackerHATVested must be less
    *    than `HUNDRED_PERCENT`.
    * @param _tokenLockFactory Address of the token lock factory to be used
    * to create a vesting contract for the approved claim reporter.
    */
    constructor(
        address _hatVaultImplementation,
        address _hatGovernance,
        address _defaultArbitrator,
        address _HAT,
        uint16 _bountyGovernanceHAT,
        uint16 _bountyHackerHATVested,
        ITokenLockFactory _tokenLockFactory
    ) {
        _transferOwnership(_hatGovernance);
        hatVaultImplementation = _hatVaultImplementation;
        HAT = IERC20(_HAT);

        validateHATSplit(_bountyGovernanceHAT, _bountyHackerHATVested);
        tokenLockFactory = _tokenLockFactory;
        generalParameters = IHATVaultsRegistry.GeneralParameters({
            hatVestingDuration: 90 days,
            hatVestingPeriods: 90,
            withdrawPeriod: 11 hours,
            safetyPeriod: 1 hours,
            setMaxBountyDelay: 2 days,
            withdrawRequestEnablePeriod: 7 days,
            withdrawRequestPendingPeriod: 7 days,
            claimFee: 0
        });

        defaultBountyGovernanceHAT = _bountyGovernanceHAT;
        defaultBountyHackerHATVested = _bountyHackerHATVested;
        defaultArbitrator = _defaultArbitrator;
        defaultChallengePeriod = 3 days;
        defaultChallengeTimeOutPeriod = 5 weeks;
        defaultArbitratorCanChangeBounty = true;
        emit RegistryCreated(
            _hatVaultImplementation,
            _HAT,
            address(_tokenLockFactory),
            generalParameters,
            _bountyGovernanceHAT,
            _bountyHackerHATVested,
            _hatGovernance,
            _defaultArbitrator,
            defaultChallengePeriod,
            defaultChallengeTimeOutPeriod,
            defaultArbitratorCanChangeBounty
        );
    }

    /** @notice See {IHATVaultsRegistry-setSwapToken}. */
    function setSwapToken(address _swapToken) external onlyOwner {
        HAT = IERC20(_swapToken);
        emit SetSwapToken(_swapToken);
    }

    /** @notice See {IHATVaultsRegistry-setEmergencyPaused}. */
    function setEmergencyPaused(bool _isEmergencyPaused) external onlyOwner {
        isEmergencyPaused = _isEmergencyPaused;
        emit SetEmergencyPaused(_isEmergencyPaused);
    }

    /** @notice See {IHATVaultsRegistry-logClaim}. */
    function logClaim(string calldata _descriptionHash) external payable {
        uint256 _claimFee = generalParameters.claimFee;
        if (_claimFee > 0) {
            if (msg.value < _claimFee)
                revert NotEnoughFeePaid();
            // solhint-disable-next-line avoid-low-level-calls
            (bool success,) = payable(owner()).call{value: msg.value}("");
            if (!success) revert ClaimFeeTransferFailed();
        }
        emit LogClaim(msg.sender, _descriptionHash);
    }

    /** @notice See {IHATVaultsRegistry-setDefaultHATBountySplit}. */
    function setDefaultHATBountySplit(
        uint16 _defaultBountyGovernanceHAT,
        uint16 _defaultBountyHackerHATVested
    ) external onlyOwner {
        validateHATSplit(_defaultBountyGovernanceHAT, _defaultBountyHackerHATVested);
        defaultBountyGovernanceHAT = _defaultBountyGovernanceHAT;
        defaultBountyHackerHATVested = _defaultBountyHackerHATVested;
        emit SetDefaultHATBountySplit(_defaultBountyGovernanceHAT, _defaultBountyHackerHATVested);

    }
   
    /** @notice See {IHATVaultsRegistry-setDefaultArbitrator}. */
    function setDefaultArbitrator(address _defaultArbitrator) external onlyOwner {
        defaultArbitrator = _defaultArbitrator;
        emit SetDefaultArbitrator(_defaultArbitrator);
    }

    /** @notice See {IHATVaultsRegistry-setDefaultChallengePeriod}. */
    function setDefaultChallengePeriod(uint32 _defaultChallengePeriod) external onlyOwner {
        validateChallengePeriod(_defaultChallengePeriod);
        defaultChallengePeriod = _defaultChallengePeriod;
        emit SetDefaultChallengePeriod(_defaultChallengePeriod);
    }

    /** @notice See {IHATVaultsRegistry-setDefaultChallengeTimeOutPeriod}. */
    function setDefaultChallengeTimeOutPeriod(uint32 _defaultChallengeTimeOutPeriod) external onlyOwner {
        validateChallengeTimeOutPeriod(_defaultChallengeTimeOutPeriod);
        defaultChallengeTimeOutPeriod = _defaultChallengeTimeOutPeriod;
        emit SetDefaultChallengeTimeOutPeriod(_defaultChallengeTimeOutPeriod);
    }

    /** @notice See {IHATVaultsRegistry-setDefaultArbitratorCanChangeBounty}. */
    function setDefaultArbitratorCanChangeBounty(bool _defaultArbitratorCanChangeBounty) external onlyOwner {
        defaultArbitratorCanChangeBounty = _defaultArbitratorCanChangeBounty;
        emit SetDefaultArbitratorCanChangeBounty(_defaultArbitratorCanChangeBounty);
    }
   
    /** @notice See {IHATVaultsRegistry-setFeeSetter}. */
    function setFeeSetter(address _feeSetter) external onlyOwner {
        feeSetter = _feeSetter;
        emit SetFeeSetter(_feeSetter);
    }

    /** @notice See {IHATVaultsRegistry-setWithdrawRequestParams}. */
    function setWithdrawRequestParams(uint32 _withdrawRequestPendingPeriod, uint32  _withdrawRequestEnablePeriod)
        external 
        onlyOwner
    {
        if (_withdrawRequestPendingPeriod > 90 days)
            revert WithdrawRequestPendingPeriodTooLong();
        if (_withdrawRequestEnablePeriod < 6 hours)
            revert WithdrawRequestEnabledPeriodTooShort();
        if (_withdrawRequestEnablePeriod > 100 days)
            revert WithdrawRequestEnabledPeriodTooLong();
        generalParameters.withdrawRequestPendingPeriod = _withdrawRequestPendingPeriod;
        generalParameters.withdrawRequestEnablePeriod = _withdrawRequestEnablePeriod;
        emit SetWithdrawRequestParams(_withdrawRequestPendingPeriod, _withdrawRequestEnablePeriod);
    }

    /** @notice See {IHATVaultsRegistry-setClaimFee}. */
    function setClaimFee(uint256 _fee) external onlyOwner {
        generalParameters.claimFee = _fee;
        emit SetClaimFee(_fee);
    }

    /** @notice See {IHATVaultsRegistry-setWithdrawSafetyPeriod}. */
    function setWithdrawSafetyPeriod(uint32 _withdrawPeriod, uint32 _safetyPeriod) external onlyOwner { 
        if (_withdrawPeriod < 1 hours) revert WithdrawPeriodTooShort();
        if (_safetyPeriod > 6 hours) revert SafetyPeriodTooLong();
        generalParameters.withdrawPeriod = _withdrawPeriod;
        generalParameters.safetyPeriod = _safetyPeriod;
        emit SetWithdrawSafetyPeriod(_withdrawPeriod, _safetyPeriod);
    }

    /** @notice See {IHATVaultsRegistry-setHatVestingParams}. */
    function setHatVestingParams(uint32 _duration, uint32 _periods) external onlyOwner {
        if (_duration >= 180 days) revert HatVestingDurationTooLong();
        if (_periods == 0) revert HatVestingPeriodsCannotBeZero();
        if (_duration < _periods) revert HatVestingDurationSmallerThanPeriods();
        generalParameters.hatVestingDuration = _duration;
        generalParameters.hatVestingPeriods = _periods;
        emit SetHatVestingParams(_duration, _periods);
    }

    /** @notice See {IHATVaultsRegistry-setMaxBountyDelay}. */
    function setMaxBountyDelay(uint32 _delay) external onlyOwner {
        if (_delay < 2 days) revert DelayTooShort();
        generalParameters.setMaxBountyDelay = _delay;
        emit SetMaxBountyDelay(_delay);
    }

    /** @notice See {IHATVaultsRegistry-createVault}. */
    function createVault(IHATVault.VaultInitParams calldata _params) external returns(address vault) {
        vault = Clones.clone(hatVaultImplementation);

        HATVault(vault).initialize(_params);

        hatVaults.push(vault);

        emit VaultCreated(vault, _params);
    }

    /** @notice See {IHATVaultsRegistry-setVaultVisibility}. */
    function setVaultVisibility(address _vault, bool _visible) external onlyOwner {
        isVaultVisible[_vault] = _visible;
        emit SetVaultVisibility(_vault, _visible);
    }

    /** @notice See {IHATVaultsRegistry-addTokensToSwap}. */
    function addTokensToSwap(
        IERC20 _asset,
        address _hacker,
        uint256 _hackersHatReward,
        uint256 _governanceHatReward
    ) external {
        hackersHatReward[address(_asset)][_hacker] += _hackersHatReward;
        governanceHatReward[address(_asset)] += _governanceHatReward;
        _asset.safeTransferFrom(msg.sender, address(this), _hackersHatReward + _governanceHatReward);
    }

    /** @notice See {IHATVaultsRegistry-swapAndSend}. */
    function swapAndSend(
        address _asset,
        address[] calldata _beneficiaries,
        uint256 _amountOutMinimum,
        address _routingContract,
        bytes calldata _routingPayload
    ) external onlyOwner {
        // Needed to avoid a "stack too deep" error
        SwapData memory _swapData;
        _swapData.hackerRewards = new uint256[](_beneficiaries.length);
        _swapData.governanceHatReward = governanceHatReward[_asset];
        _swapData.amount = _swapData.governanceHatReward;
        for (uint256 i = 0; i < _beneficiaries.length;) { 
            _swapData.hackerRewards[i] = hackersHatReward[_asset][_beneficiaries[i]];
            hackersHatReward[_asset][_beneficiaries[i]] = 0;
            _swapData.amount += _swapData.hackerRewards[i]; 
            unchecked { ++i; }
        }
        if (_swapData.amount == 0) revert AmountToSwapIsZero();
        IERC20 _HAT = HAT;
        (_swapData.hatsReceived, _swapData.amountUnused) = _swapTokenForHAT(IERC20(_asset), _swapData.amount, _amountOutMinimum, _routingContract, _routingPayload);
        
        _swapData.usedPart = (_swapData.amount - _swapData.amountUnused);
        _swapData.governanceAmountSwapped = _swapData.usedPart.mulDiv(_swapData.governanceHatReward, _swapData.amount);
        governanceHatReward[_asset]  = _swapData.amountUnused.mulDiv(_swapData.governanceHatReward, _swapData.amount);

        for (uint256 i = 0; i < _beneficiaries.length;) {
            uint256 _hackerReward = _swapData.hatsReceived.mulDiv(_swapData.hackerRewards[i], _swapData.amount);
            uint256 _hackerAmountSwapped = _swapData.usedPart.mulDiv(_swapData.hackerRewards[i], _swapData.amount);
            _swapData.totalHackerReward += _hackerReward;
            hackersHatReward[_asset][_beneficiaries[i]] = _swapData.amountUnused.mulDiv(_swapData.hackerRewards[i], _swapData.amount);
            address _tokenLock;
            if (_hackerReward > 0) {
                // hacker gets her reward via vesting contract
                _tokenLock = tokenLockFactory.createTokenLock(
                    address(_HAT),
                    0x0000000000000000000000000000000000000000, //this address as owner, so it can do nothing.
                    _beneficiaries[i],
                    _hackerReward,
                    // solhint-disable-next-line not-rely-on-time
                    block.timestamp, //start
                    // solhint-disable-next-line not-rely-on-time
                    block.timestamp + generalParameters.hatVestingDuration, //end
                    generalParameters.hatVestingPeriods,
                    0, // no release start
                    0, // no cliff
                    ITokenLock.Revocability.Disabled,
                    true
                );
                _HAT.safeTransfer(_tokenLock, _hackerReward);
            }
            emit SwapAndSend(_beneficiaries[i], _hackerAmountSwapped, _hackerReward, _tokenLock);
            unchecked { ++i; }
        }
        address _owner = owner(); 
        uint256 _amountToOwner = _swapData.hatsReceived - _swapData.totalHackerReward;
        _HAT.safeTransfer(_owner, _amountToOwner);
        emit SwapAndSend(_owner, _swapData.governanceAmountSwapped, _amountToOwner, address(0));
    }

    /** @notice See {IHATVaultsRegistry-getWithdrawPeriod}. */   
      function getWithdrawPeriod() external view returns (uint256) {
        return generalParameters.withdrawPeriod;
    }

    /** @notice See {IHATVaultsRegistry-getSafetyPeriod}. */   
    function getSafetyPeriod() external view returns (uint256) {
        return generalParameters.safetyPeriod;
    }

    /** @notice See {IHATVaultsRegistry-getWithdrawRequestEnablePeriod}. */   
    function getWithdrawRequestEnablePeriod() external view returns (uint256) {
        return generalParameters.withdrawRequestEnablePeriod;
    }

    /** @notice See {IHATVaultsRegistry-getWithdrawRequestPendingPeriod}. */   
    function getWithdrawRequestPendingPeriod() external view returns (uint256) {
        return generalParameters.withdrawRequestPendingPeriod;
    }

    /** @notice See {IHATVaultsRegistry-getSetMaxBountyDelay}. */   
    function getSetMaxBountyDelay() external view returns (uint256) {
        return generalParameters.setMaxBountyDelay;
    }

    /** @notice See {IHATVaultsRegistry-getNumberOfVaults}. */
    function getNumberOfVaults() external view returns(uint256) {
        return hatVaults.length;
    }

    /** @notice See {IHATVaultsRegistry-validateHATSplit}. */
    function validateHATSplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) public pure {
        if (_bountyGovernanceHAT + _bountyHackerHATVested > MAX_HAT_SPLIT)
            revert TotalHatsSplitPercentageShouldBeUpToMaxHATSplit();
    }

    /** @notice See {IHATVaultsRegistry-validateChallengePeriod}. */
    function validateChallengePeriod(uint32 _challengePeriod) public pure {
        if (_challengePeriod < 1 days) revert ChallengePeriodTooShort();
        if (_challengePeriod > 5 days) revert ChallengePeriodTooLong();
    }

    /** @notice See {IHATVaultsRegistry-validateChallengeTimeOutPeriod}. */
    function validateChallengeTimeOutPeriod(uint32 _challengeTimeOutPeriod) public pure {
        if (_challengeTimeOutPeriod < 2 days) revert ChallengeTimeOutPeriodTooShort();
        if (_challengeTimeOutPeriod > 85 days) revert ChallengeTimeOutPeriodTooLong();
    }
    
    /**
    * @dev Use the given routing contract to swap the given token to HAT token
    * @param _asset The token to swap
    * @param _amount Amount of token to swap
    * @param _amountOutMinimum Minimum amount of HAT tokens at swap
    * @param _routingContract Routing contract to call for the swap
    * @param _routingPayload Payload to send to the _routingContract for the 
    * swap
    */
    function _swapTokenForHAT(
        IERC20 _asset,
        uint256 _amount,
        uint256 _amountOutMinimum,
        address _routingContract,
        bytes calldata _routingPayload)
    internal
    returns (uint256 hatsReceived, uint256 amountUnused)
    {
        IERC20 _HAT = HAT;
        if (_asset == _HAT) {
            return (_amount, 0);
        }

        IERC20(_asset).safeApprove(_routingContract, _amount);
        uint256 _balanceBefore = _HAT.balanceOf(address(this));
        uint256 _assetBalanceBefore = _asset.balanceOf(address(this));

        // solhint-disable-next-line avoid-low-level-calls
        (bool success,) = _routingContract.call(_routingPayload);
        if (!success) revert SwapFailed();
        hatsReceived = _HAT.balanceOf(address(this)) - _balanceBefore;
        amountUnused = _amount - (_assetBalanceBefore - _asset.balanceOf(address(this)));
        if (hatsReceived < _amountOutMinimum)
            revert AmountSwappedLessThanMinimum();

        IERC20(_asset).safeApprove(address(_routingContract), 0);
    }
}

File 2 of 29 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 3 of 29 : IERC4626Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20Upgradeable.sol";
import "../token/ERC20/extensions/IERC20MetadataUpgradeable.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 *
 * _Available since v4.7._
 */
interface IERC4626Upgradeable is IERC20Upgradeable, IERC20MetadataUpgradeable {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);
}

File 4 of 29 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

File 5 of 29 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 6 of 29 : ERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[45] private __gap;
}

File 7 of 29 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 8 of 29 : ERC4626Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (token/ERC20/extensions/ERC4626.sol)

pragma solidity ^0.8.0;

import "../ERC20Upgradeable.sol";
import "../utils/SafeERC20Upgradeable.sol";
import "../../../interfaces/IERC4626Upgradeable.sol";
import "../../../utils/math/MathUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
 *
 * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
 * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
 * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
 * contract and not the "assets" token which is an independent contract.
 *
 * CAUTION: When the vault is empty or nearly empty, deposits are at high risk of being stolen through frontrunning with
 * a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
 * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
 * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
 * similarly be affected by slippage. Users can protect against this attack as well unexpected slippage in general by
 * verifying the amount received is as expected, using a wrapper that performs these checks such as
 * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
 *
 * _Available since v4.7._
 */
abstract contract ERC4626Upgradeable is Initializable, ERC20Upgradeable, IERC4626Upgradeable {
    using MathUpgradeable for uint256;

    IERC20Upgradeable private _asset;
    uint8 private _decimals;

    /**
     * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
     */
    function __ERC4626_init(IERC20Upgradeable asset_) internal onlyInitializing {
        __ERC4626_init_unchained(asset_);
    }

    function __ERC4626_init_unchained(IERC20Upgradeable asset_) internal onlyInitializing {
        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
        _decimals = success ? assetDecimals : super.decimals();
        _asset = asset_;
    }

    /**
     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
     */
    function _tryGetAssetDecimals(IERC20Upgradeable asset_) private view returns (bool, uint8) {
        (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
            abi.encodeWithSelector(IERC20MetadataUpgradeable.decimals.selector)
        );
        if (success && encodedDecimals.length >= 32) {
            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
            if (returnedDecimals <= type(uint8).max) {
                return (true, uint8(returnedDecimals));
            }
        }
        return (false, 0);
    }

    /**
     * @dev Decimals are read from the underlying asset in the constructor and cached. If this fails (e.g., the asset
     * has not been created yet), the cached value is set to a default obtained by `super.decimals()` (which depends on
     * inheritance but is most likely 18). Override this function in order to set a guaranteed hardcoded value.
     * See {IERC20Metadata-decimals}.
     */
    function decimals() public view virtual override(IERC20MetadataUpgradeable, ERC20Upgradeable) returns (uint8) {
        return _decimals;
    }

    /** @dev See {IERC4626-asset}. */
    function asset() public view virtual override returns (address) {
        return address(_asset);
    }

    /** @dev See {IERC4626-totalAssets}. */
    function totalAssets() public view virtual override returns (uint256) {
        return _asset.balanceOf(address(this));
    }

    /** @dev See {IERC4626-convertToShares}. */
    function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {
        return _convertToShares(assets, MathUpgradeable.Rounding.Down);
    }

    /** @dev See {IERC4626-convertToAssets}. */
    function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {
        return _convertToAssets(shares, MathUpgradeable.Rounding.Down);
    }

    /** @dev See {IERC4626-maxDeposit}. */
    function maxDeposit(address) public view virtual override returns (uint256) {
        return _isVaultCollateralized() ? type(uint256).max : 0;
    }

    /** @dev See {IERC4626-maxMint}. */
    function maxMint(address) public view virtual override returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxWithdraw}. */
    function maxWithdraw(address owner) public view virtual override returns (uint256) {
        return _convertToAssets(balanceOf(owner), MathUpgradeable.Rounding.Down);
    }

    /** @dev See {IERC4626-maxRedeem}. */
    function maxRedeem(address owner) public view virtual override returns (uint256) {
        return balanceOf(owner);
    }

    /** @dev See {IERC4626-previewDeposit}. */
    function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
        return _convertToShares(assets, MathUpgradeable.Rounding.Down);
    }

    /** @dev See {IERC4626-previewMint}. */
    function previewMint(uint256 shares) public view virtual override returns (uint256) {
        return _convertToAssets(shares, MathUpgradeable.Rounding.Up);
    }

    /** @dev See {IERC4626-previewWithdraw}. */
    function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
        return _convertToShares(assets, MathUpgradeable.Rounding.Up);
    }

    /** @dev See {IERC4626-previewRedeem}. */
    function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
        return _convertToAssets(shares, MathUpgradeable.Rounding.Down);
    }

    /** @dev See {IERC4626-deposit}. */
    function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {
        require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");

        uint256 shares = previewDeposit(assets);
        _deposit(_msgSender(), receiver, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-mint}.
     *
     * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
     * In this case, the shares will be minted without requiring any assets to be deposited.
     */
    function mint(uint256 shares, address receiver) public virtual override returns (uint256) {
        require(shares <= maxMint(receiver), "ERC4626: mint more than max");

        uint256 assets = previewMint(shares);
        _deposit(_msgSender(), receiver, assets, shares);

        return assets;
    }

    /** @dev See {IERC4626-withdraw}. */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual override returns (uint256) {
        require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");

        uint256 shares = previewWithdraw(assets);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-redeem}. */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual override returns (uint256) {
        require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");

        uint256 assets = previewRedeem(shares);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return assets;
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     *
     * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset
     * would represent an infinite amount of shares.
     */
    function _convertToShares(uint256 assets, MathUpgradeable.Rounding rounding) internal view virtual returns (uint256 shares) {
        uint256 supply = totalSupply();
        return
            (assets == 0 || supply == 0)
                ? _initialConvertToShares(assets, rounding)
                : assets.mulDiv(supply, totalAssets(), rounding);
    }

    /**
     * @dev Internal conversion function (from assets to shares) to apply when the vault is empty.
     *
     * NOTE: Make sure to keep this function consistent with {_initialConvertToAssets} when overriding it.
     */
    function _initialConvertToShares(
        uint256 assets,
        MathUpgradeable.Rounding /*rounding*/
    ) internal view virtual returns (uint256 shares) {
        return assets;
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function _convertToAssets(uint256 shares, MathUpgradeable.Rounding rounding) internal view virtual returns (uint256 assets) {
        uint256 supply = totalSupply();
        return
            (supply == 0) ? _initialConvertToAssets(shares, rounding) : shares.mulDiv(totalAssets(), supply, rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) to apply when the vault is empty.
     *
     * NOTE: Make sure to keep this function consistent with {_initialConvertToShares} when overriding it.
     */
    function _initialConvertToAssets(
        uint256 shares,
        MathUpgradeable.Rounding /*rounding*/
    ) internal view virtual returns (uint256 assets) {
        return shares;
    }

    /**
     * @dev Deposit/mint common workflow.
     */
    function _deposit(
        address caller,
        address receiver,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the
        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
        // assets are transferred and before the shares are minted, which is a valid state.
        // slither-disable-next-line reentrancy-no-eth
        SafeERC20Upgradeable.safeTransferFrom(_asset, caller, address(this), assets);
        _mint(receiver, shares);

        emit Deposit(caller, receiver, assets, shares);
    }

    /**
     * @dev Withdraw/redeem common workflow.
     */
    function _withdraw(
        address caller,
        address receiver,
        address owner,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        if (caller != owner) {
            _spendAllowance(owner, caller, shares);
        }

        // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
        // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
        // shares are burned and after the assets are transferred, which is a valid state.
        _burn(owner, shares);
        SafeERC20Upgradeable.safeTransfer(_asset, receiver, assets);

        emit Withdraw(caller, receiver, owner, assets, shares);
    }

    /**
     * @dev Checks if vault is "healthy" in the sense of having assets backing the circulating shares.
     */
    function _isVaultCollateralized() private view returns (bool) {
        return totalAssets() > 0 || totalSupply() == 0;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 9 of 29 : IERC20MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @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);
}

File 10 of 29 : draft-IERC20PermitUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20PermitUpgradeable {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 11 of 29 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../extensions/draft-IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
    using AddressUpgradeable for address;

    function safeTransfer(
        IERC20Upgradeable token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20Upgradeable token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20PermitUpgradeable token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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(IERC20Upgradeable 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, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 12 of 29 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 13 of 29 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 14 of 29 : MathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @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 up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (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; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            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 (rounding == Rounding.Up && 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 down.
     *
     * 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * 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;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 15 of 29 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 16 of 29 : Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

File 17 of 29 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 18 of 29 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 19 of 29 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../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;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 20 of 29 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 21 of 29 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 22 of 29 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @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 up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (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; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            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 (rounding == Rounding.Up && 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 down.
     *
     * 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * 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;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 23 of 29 : HATVault.sol
// SPDX-License-Identifier: MIT
// Disclaimer https://github.com/hats-finance/hats-contracts/blob/main/DISCLAIMER.md

pragma solidity 0.8.16;

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./tokenlock/TokenLockFactory.sol";
import "./interfaces/IHATVault.sol";
import "./interfaces/IRewardController.sol";
import "./HATVaultsRegistry.sol";

/** @title A Hats.finance vault which holds the funds for a specific project's
* bug bounties
* @author Hats.finance
* @notice The HATVault can be deposited into in a permissionless manner using
* the vault’s native token. When a bug is submitted and approved, the bounty 
* is paid out using the funds in the vault. Bounties are paid out as a
* percentage of the vault. The percentage is set according to the severity of
* the bug. Vaults have regular safety periods (typically for an hour twice a
* day) which are time for the committee to make decisions.
*
* In addition to the roles defined in the HATVaultsRegistry, every HATVault 
* has the roles:
* Committee - The only address which can submit a claim for a bounty payout
* and set the maximum bounty.
* User - Anyone can deposit the vault's native token into the vault and 
* recieve shares for it. Shares represent the user's relative part in the
* vault, and when a bounty is paid out, users lose part of their deposits
* (based on percentage paid), but keep their share of the vault.
* Users also receive rewards for their deposits, which can be claimed at any
* time.
* To withdraw previously deposited tokens, a user must first send a withdraw
* request, and the withdrawal will be made available after a pending period.
* Withdrawals are not permitted during safety periods or while there is an 
* active claim for a bounty payout.
*
* Bounties are payed out distributed between a few channels, and that 
* distribution is set upon creation (the hacker gets part in direct transfer,
* part in vested reward and part in vested HAT token, part gets rewarded to
* the committee, part gets swapped to HAT token and burned and/or sent to Hats
* governance).
*
* This project is open-source and can be found at:
* https://github.com/hats-finance/hats-contracts
*/
contract HATVault is IHATVault, ERC4626Upgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable {
    using SafeERC20 for IERC20;
    using MathUpgradeable for uint256;

    struct Claim {
        bytes32 claimId;
        address beneficiary;
        uint16 bountyPercentage;
        // the address of the committee at the time of the submission, so that this committee will
        // be paid their share of the bounty in case the committee changes before claim approval
        address committee;
        uint32 createdAt;
        uint32 challengedAt;
        uint256 bountyGovernanceHAT;
        uint256 bountyHackerHATVested;
        address arbitrator;
        uint32 challengePeriod;
        uint32 challengeTimeOutPeriod;
        bool arbitratorCanChangeBounty;
    }

    struct PendingMaxBounty {
        uint16 maxBounty;
        uint32 timestamp;
    }

    uint256 public constant MAX_UINT = type(uint256).max;
    uint16 public constant NULL_UINT16 = type(uint16).max;
    uint32 public constant NULL_UINT32 = type(uint32).max;
    address public constant NULL_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
    uint256 public constant HUNDRED_PERCENT = 1e4;
    uint256 public constant HUNDRED_PERCENT_SQRD = 1e8;
    uint256 public constant MAX_BOUNTY_LIMIT = 90e2; // Max bounty can be up to 90%
    uint256 public constant MAX_WITHDRAWAL_FEE = 2e2; // Max fee is 2%
    uint256 public constant MAX_COMMITTEE_BOUNTY = 10e2; // Max committee bounty can be up to 10%

    uint256 public constant MINIMAL_AMOUNT_OF_SHARES = 1e3; // to reduce rounding errors, the number of shares is either 0, or > than this number

    HATVaultsRegistry public registry;
    ITokenLockFactory public tokenLockFactory;

    Claim public activeClaim;

    IRewardController[] public rewardControllers;

    IHATVault.BountySplit public bountySplit;
    uint16 public maxBounty;
    uint32 public vestingDuration;
    uint32 public vestingPeriods;
    address public committee;

    bool public committeeCheckedIn;
    bool public depositPause;
    uint256 public withdrawalFee;

    uint256 internal nonce;

    PendingMaxBounty public pendingMaxBounty;


    // Time of when withdrawal period starts for every user that has an
    // active withdraw request. (time when last withdraw request pending 
    // period ended, or 0 if last action was deposit or withdraw)
    mapping(address => uint256) public withdrawEnableStartTime;

    // the percentage of the total bounty to be swapped to HATs and sent to governance (out of {HUNDRED_PERCENT})
    uint16 internal bountyGovernanceHAT;
    // the percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract (out of {HUNDRED_PERCENT})
    uint16 internal bountyHackerHATVested;

    // address of the arbitrator - which can dispute claims and override the committee's decisions
    address internal arbitrator;
    // time during which a claim can be challenged by the arbitrator
    uint32 internal challengePeriod;
    // time after which a challenged claim is automatically dismissed
    uint32 internal challengeTimeOutPeriod;
    // whether the arbitrator can change bounty of claims
    ArbitratorCanChangeBounty internal arbitratorCanChangeBounty;

    bool private _isEmergencyWithdraw;

    modifier onlyRegistryOwner() {
        if (registry.owner() != msg.sender) revert OnlyRegistryOwner();
        _;
    }

    modifier onlyFeeSetter() {
        if (registry.feeSetter() != msg.sender) revert OnlyFeeSetter();
        _;
    }

    modifier onlyCommittee() {
        if (committee != msg.sender) revert OnlyCommittee();
        _;
    }

    modifier notEmergencyPaused() {
        if (registry.isEmergencyPaused()) revert SystemInEmergencyPause();
        _;
    }

    modifier noSafetyPeriod() {
        uint256 _withdrawPeriod = registry.getWithdrawPeriod();
        // disable withdraw for safetyPeriod (e.g 1 hour) after each withdrawPeriod(e.g 11 hours)
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp % (_withdrawPeriod + registry.getSafetyPeriod()) >= _withdrawPeriod)
            revert SafetyPeriod();
        _;
    }

    modifier noActiveClaim() {
        if (activeClaim.createdAt != 0) revert ActiveClaimExists();
        _;
    }

    modifier isActiveClaim(bytes32 _claimId) {
        if (activeClaim.createdAt == 0) revert NoActiveClaimExists();
        if (activeClaim.claimId != _claimId) revert ClaimIdIsNotActive();
        _;
    }

    /** @notice See {IHATVault-initialize}. */
    function initialize(IHATVault.VaultInitParams calldata _params) external initializer {
        if (_params.maxBounty > MAX_BOUNTY_LIMIT)
            revert MaxBountyCannotBeMoreThanMaxBountyLimit();
        _validateSplit(_params.bountySplit);
        __ERC20_init(string.concat("Hats Vault ", _params.name), string.concat("HAT", _params.symbol));
        __ERC4626_init(IERC20MetadataUpgradeable(address(_params.asset)));
        rewardControllers = _params.rewardControllers;
        _setVestingParams(_params.vestingDuration, _params.vestingPeriods);
        HATVaultsRegistry _registry = HATVaultsRegistry(msg.sender);
        maxBounty = _params.maxBounty;
        bountySplit = _params.bountySplit;
        committee = _params.committee;
        depositPause = _params.isPaused;
        registry = _registry;
        __ReentrancyGuard_init();
        _transferOwnership(_params.owner);
        tokenLockFactory = _registry.tokenLockFactory();

        // Set vault to use default registry values where applicable
        arbitrator = NULL_ADDRESS;
        bountyGovernanceHAT = NULL_UINT16;
        bountyHackerHATVested = NULL_UINT16;
        arbitratorCanChangeBounty = ArbitratorCanChangeBounty.DEFAULT;
        challengePeriod = NULL_UINT32;
        challengeTimeOutPeriod = NULL_UINT32;

        emit SetVaultDescription(_params.descriptionHash);
    }


    /* ---------------------------------- Claim --------------------------------------- */

    /** @notice See {IHATVault-submitClaim}. */
    function submitClaim(address _beneficiary, uint16 _bountyPercentage, string calldata _descriptionHash)
        external onlyCommittee noActiveClaim notEmergencyPaused returns (bytes32 claimId) {
        HATVaultsRegistry _registry = registry;
        uint256 withdrawPeriod = _registry.getWithdrawPeriod();
        // require we are in safetyPeriod
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp % (withdrawPeriod + _registry.getSafetyPeriod()) < withdrawPeriod)
            revert NotSafetyPeriod();
        if (_bountyPercentage > maxBounty)
            revert BountyPercentageHigherThanMaxBounty();
        claimId = keccak256(abi.encodePacked(address(this), ++nonce));
        activeClaim = Claim({
            claimId: claimId,
            beneficiary: _beneficiary,
            bountyPercentage: _bountyPercentage,
            committee: msg.sender,
            // solhint-disable-next-line not-rely-on-time
            createdAt: uint32(block.timestamp),
            challengedAt: 0,
            bountyGovernanceHAT: getBountyGovernanceHAT(),
            bountyHackerHATVested: getBountyHackerHATVested(),
            arbitrator: getArbitrator(),
            challengePeriod: getChallengePeriod(),
            challengeTimeOutPeriod: getChallengeTimeOutPeriod(),
            arbitratorCanChangeBounty: getArbitratorCanChangeBounty()
        });

        emit SubmitClaim(
            claimId,
            msg.sender,
            _beneficiary,
            _bountyPercentage,
            _descriptionHash
        );
    }

    function challengeClaim(bytes32 _claimId) external isActiveClaim(_claimId) {
        if (msg.sender != activeClaim.arbitrator && msg.sender != registry.owner())
            revert OnlyArbitratorOrRegistryOwner();
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp >= activeClaim.createdAt + activeClaim.challengePeriod)
            revert ChallengePeriodEnded();
        if (activeClaim.challengedAt != 0) {
            revert ClaimAlreadyChallenged();
        } 
        // solhint-disable-next-line not-rely-on-time
        activeClaim.challengedAt = uint32(block.timestamp);
        emit ChallengeClaim(_claimId);
    }

    /** @notice See {IHATVault-approveClaim}. */
    function approveClaim(bytes32 _claimId, uint16 _bountyPercentage) external nonReentrant isActiveClaim(_claimId) {
        Claim memory _claim = activeClaim;
        delete activeClaim;
        
        
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp >= _claim.createdAt + _claim.challengePeriod + _claim.challengeTimeOutPeriod) {
            // cannot approve an expired claim
            revert ClaimExpired();
        } 
        if (_claim.challengedAt != 0) {
            // the claim was challenged, and only the arbitrator can approve it, within the timeout period
            if (
                msg.sender != _claim.arbitrator ||
                // solhint-disable-next-line not-rely-on-time
                block.timestamp >= _claim.challengedAt + _claim.challengeTimeOutPeriod
            )
                revert ChallengedClaimCanOnlyBeApprovedByArbitratorUntilChallengeTimeoutPeriod();
            // the arbitrator can update the bounty if needed
            if (_claim.arbitratorCanChangeBounty && _bountyPercentage != 0) {
                _claim.bountyPercentage = _bountyPercentage;
            }
        } else {
            // the claim can be approved by anyone if the challengePeriod passed without a challenge
            if (
                // solhint-disable-next-line not-rely-on-time
                block.timestamp <= _claim.createdAt + _claim.challengePeriod
            ) 
                revert UnchallengedClaimCanOnlyBeApprovedAfterChallengePeriod();
        }

        address tokenLock;

        IHATVault.ClaimBounty memory claimBounty = _calcClaimBounty(
            _claim.bountyPercentage,
            _claim.bountyGovernanceHAT,
            _claim.bountyHackerHATVested
        );

        IERC20 _asset = IERC20(asset());
        if (claimBounty.hackerVested > 0) {
            //hacker gets part of bounty to a vesting contract
            tokenLock = tokenLockFactory.createTokenLock(
                address(_asset),
                0x0000000000000000000000000000000000000000, //this address as owner, so it can do nothing.
                _claim.beneficiary,
                claimBounty.hackerVested,
                // solhint-disable-next-line not-rely-on-time
                block.timestamp, //start
                // solhint-disable-next-line not-rely-on-time
                block.timestamp + vestingDuration, //end
                vestingPeriods,
                0, //no release start
                0, //no cliff
                ITokenLock.Revocability.Disabled,
                false
            );
            _asset.safeTransfer(tokenLock, claimBounty.hackerVested);
        }

        _asset.safeTransfer(_claim.beneficiary, claimBounty.hacker);
        _asset.safeTransfer(_claim.committee, claimBounty.committee);

        // send to the registry the amount of tokens which should be swapped 
        // to HAT so it could call swapAndSend in a separate tx.
        HATVaultsRegistry _registry = registry;
        _asset.safeApprove(address(_registry), claimBounty.hackerHatVested + claimBounty.governanceHat);
        _registry.addTokensToSwap(
            _asset,
            _claim.beneficiary,
            claimBounty.hackerHatVested,
            claimBounty.governanceHat
        );

        // make sure to reset approval
        _asset.safeApprove(address(_registry), 0);

        emit ApproveClaim(
            _claimId,
            msg.sender,
            _claim.beneficiary,
            _claim.bountyPercentage,
            tokenLock,
            claimBounty
        );
    }

    /** @notice See {IHATVault-dismissClaim}. */
    function dismissClaim(bytes32 _claimId) external isActiveClaim(_claimId) {
        uint256 _challengeTimeOutPeriod = activeClaim.challengeTimeOutPeriod;
        uint256 _challengedAt = activeClaim.challengedAt;
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp <= activeClaim.createdAt + activeClaim.challengePeriod + _challengeTimeOutPeriod) {
            if (_challengedAt == 0) revert OnlyCallableIfChallenged();
            if (
                // solhint-disable-next-line not-rely-on-time
                block.timestamp <= _challengedAt + _challengeTimeOutPeriod && 
                msg.sender != activeClaim.arbitrator
            ) revert OnlyCallableByArbitratorOrAfterChallengeTimeOutPeriod();
        } // else the claim is expired and should be dismissed
        delete activeClaim;

        emit DismissClaim(_claimId);
    }
    /* -------------------------------------------------------------------------------- */

    /* ---------------------------------- Params -------------------------------------- */

    /** @notice See {IHATVault-setCommittee}. */
    function setCommittee(address _committee) external {
        // vault owner can update committee only if committee was not checked in yet.
        if (msg.sender == owner() && committee != msg.sender) {
            if (committeeCheckedIn)
                revert CommitteeAlreadyCheckedIn();
        } else {
            if (committee != msg.sender) revert OnlyCommittee();
        }

        committee = _committee;

        emit SetCommittee(_committee);
    }

    /** @notice See {IHATVault-setVestingParams}. */
    function setVestingParams(uint32 _duration, uint32 _periods) external onlyOwner {
        _setVestingParams(_duration, _periods);
    }

    /** @notice See {IHATVault-setBountySplit}. */
    function setBountySplit(IHATVault.BountySplit calldata _bountySplit) external onlyOwner noActiveClaim noSafetyPeriod {
        _validateSplit(_bountySplit);
        bountySplit = _bountySplit;
        emit SetBountySplit(_bountySplit);
    }

    /** @notice See {IHATVault-setWithdrawalFee}. */
    function setWithdrawalFee(uint256 _fee) external onlyFeeSetter {
        if (_fee > MAX_WITHDRAWAL_FEE) revert WithdrawalFeeTooBig();
        withdrawalFee = _fee;
        emit SetWithdrawalFee(_fee);
    }

    /** @notice See {IHATVault-committeeCheckIn}. */
    function committeeCheckIn() external onlyCommittee {
        committeeCheckedIn = true;
        emit CommitteeCheckedIn();
    }

    /** @notice See {IHATVault-setPendingMaxBounty}. */
    function setPendingMaxBounty(uint16 _maxBounty) external onlyOwner noActiveClaim {
        if (_maxBounty > MAX_BOUNTY_LIMIT)
            revert MaxBountyCannotBeMoreThanMaxBountyLimit();
        pendingMaxBounty.maxBounty = _maxBounty;
        // solhint-disable-next-line not-rely-on-time
        pendingMaxBounty.timestamp = uint32(block.timestamp);
        emit SetPendingMaxBounty(_maxBounty);
    }

    /** @notice See {IHATVault-setMaxBounty}. */
    function setMaxBounty() external onlyOwner noActiveClaim {
        PendingMaxBounty memory _pendingMaxBounty = pendingMaxBounty;
        if (_pendingMaxBounty.timestamp == 0) revert NoPendingMaxBounty();

        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp - _pendingMaxBounty.timestamp < registry.getSetMaxBountyDelay())
            revert DelayPeriodForSettingMaxBountyHadNotPassed();

        uint16 _maxBounty = pendingMaxBounty.maxBounty;
        maxBounty = _maxBounty;
        delete pendingMaxBounty;
        emit SetMaxBounty(_maxBounty);
    }

    /** @notice See {IHATVault-setDepositPause}. */
    function setDepositPause(bool _depositPause) external onlyOwner {
        depositPause = _depositPause;
        emit SetDepositPause(_depositPause);
    }

    /** @notice See {IHATVault-setVaultDescription}. */
    function setVaultDescription(string calldata _descriptionHash) external onlyRegistryOwner {
        emit SetVaultDescription(_descriptionHash);
    }

    /** @notice See {IHATVault-addRewardController}. */
    function addRewardController(IRewardController _rewardController) external onlyRegistryOwner noActiveClaim {
        for (uint256 i = 0; i < rewardControllers.length;) { 
            if (_rewardController == rewardControllers[i]) revert DuplicatedRewardController();
            unchecked { ++i; }
        }
        rewardControllers.push(_rewardController);
        emit AddRewardController(_rewardController);
    }
    
    /** @notice See {IHATVault-setHATBountySplit}. */
    function setHATBountySplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) external onlyRegistryOwner {
        bountyGovernanceHAT = _bountyGovernanceHAT;
        bountyHackerHATVested = _bountyHackerHATVested;

        registry.validateHATSplit(getBountyGovernanceHAT(), getBountyHackerHATVested());

        emit SetHATBountySplit(_bountyGovernanceHAT, _bountyHackerHATVested);
    }

    /** @notice See {IHATVault-setArbitrator}. */
    function setArbitrator(address _arbitrator) external onlyRegistryOwner {
        arbitrator = _arbitrator;
        emit SetArbitrator(_arbitrator);
    }

    /** @notice See {IHATVault-setChallengePeriod}. */
    function setChallengePeriod(uint32 _challengePeriod) external onlyRegistryOwner {
        if (_challengePeriod != NULL_UINT32) {
            registry.validateChallengePeriod(_challengePeriod);
        }

        challengePeriod = _challengePeriod;
        
        emit SetChallengePeriod(_challengePeriod);
    }

    /** @notice See {IHATVault-setChallengeTimeOutPeriod}. */
    function setChallengeTimeOutPeriod(uint32 _challengeTimeOutPeriod) external onlyRegistryOwner {
        if (_challengeTimeOutPeriod != NULL_UINT32) {
            registry.validateChallengeTimeOutPeriod(_challengeTimeOutPeriod);
        }

        challengeTimeOutPeriod = _challengeTimeOutPeriod;
        
        emit SetChallengeTimeOutPeriod(_challengeTimeOutPeriod);
    }

    /** @notice See {IHATVault-setArbitratorCanChangeBounty}. */
    function setArbitratorCanChangeBounty(ArbitratorCanChangeBounty _arbitratorCanChangeBounty) external onlyRegistryOwner {
        arbitratorCanChangeBounty = _arbitratorCanChangeBounty;
        emit SetArbitratorCanChangeBounty(_arbitratorCanChangeBounty);
    }

    /* -------------------------------------------------------------------------------- */

    /* ---------------------------------- Vault --------------------------------------- */

    /** @notice See {IHATVault-withdrawRequest}. */
    function withdrawRequest() external nonReentrant {
        // set the withdrawEnableStartTime time to be withdrawRequestPendingPeriod from now
        // solhint-disable-next-line not-rely-on-time
        uint256 _withdrawEnableStartTime = block.timestamp + registry.getWithdrawRequestPendingPeriod();
        address msgSender = _msgSender();
        withdrawEnableStartTime[msgSender] = _withdrawEnableStartTime;
        emit WithdrawRequest(msgSender, _withdrawEnableStartTime);
    }

    /** @notice See {IHATVault-withdrawAndClaim}. */
    function withdrawAndClaim(uint256 assets, address receiver, address owner) external returns (uint256 shares) {
        shares = withdraw(assets, receiver, owner);
        for (uint256 i = 0; i < rewardControllers.length;) { 
            rewardControllers[i].claimReward(address(this), owner);
            unchecked { ++i; }
        }
    }

    /** @notice See {IHATVault-redeemAndClaim}. */
    function redeemAndClaim(uint256 shares, address receiver, address owner) external returns (uint256 assets) {
        assets = redeem(shares, receiver, owner);
        for (uint256 i = 0; i < rewardControllers.length;) { 
            rewardControllers[i].claimReward(address(this), owner);
            unchecked { ++i; }
        }
    }

    /** @notice See {IHATVault-emergencyWithdraw}. */
    function emergencyWithdraw(address receiver) external returns (uint256 assets) {
        _isEmergencyWithdraw = true;
        address msgSender = _msgSender();
        assets = redeem(balanceOf(msgSender), receiver, msgSender);
        _isEmergencyWithdraw = false;
    }

    /** @notice See {IHATVault-withdraw}. */
    function withdraw(uint256 assets, address receiver, address owner) 
        public override(IHATVault, ERC4626Upgradeable) virtual returns (uint256) {
        (uint256 _shares, uint256 _fee) = previewWithdrawAndFee(assets);
        _withdraw(_msgSender(), receiver, owner, assets, _shares, _fee);

        return _shares;
    }

    /** @notice See {IHATVault-redeem}. */
    function redeem(uint256 shares, address receiver, address owner) 
        public override(IHATVault, ERC4626Upgradeable) virtual returns (uint256) {
        (uint256 _assets, uint256 _fee) = previewRedeemAndFee(shares);
        _withdraw(_msgSender(), receiver, owner, _assets, shares, _fee);

        return _assets;
    }

    /** @notice See {IHATVault-deposit}. */
    function deposit(uint256 assets, address receiver) public override(IHATVault, ERC4626Upgradeable) virtual returns (uint256) {
        return super.deposit(assets, receiver);
    }

    /** @notice See {IHATVault-withdraw}. */
    function withdraw(uint256 assets, address receiver, address owner, uint256 maxShares) public virtual returns (uint256) {
        uint256 shares = withdraw(assets, receiver, owner);
        if (shares > maxShares) revert WithdrawSlippageProtection();
        return shares;
    }

    /** @notice See {IHATVault-redeem}. */
    function redeem(uint256 shares, address receiver, address owner, uint256 minAssets) public virtual returns (uint256) {
        uint256 assets = redeem(shares, receiver, owner);
        if (assets < minAssets) revert RedeemSlippageProtection();
        return assets;
    }

    /** @notice See {IHATVault-withdrawAndClaim}. */
    function withdrawAndClaim(uint256 assets, address receiver, address owner, uint256 maxShares) external returns (uint256 shares) {
        shares = withdraw(assets, receiver, owner, maxShares);
        for (uint256 i = 0; i < rewardControllers.length;) { 
            rewardControllers[i].claimReward(address(this), owner);
            unchecked { ++i; }
        }
    }

    /** @notice See {IHATVault-redeemAndClaim}. */
    function redeemAndClaim(uint256 shares, address receiver, address owner, uint256 minAssets) external returns (uint256 assets) {
        assets = redeem(shares, receiver, owner, minAssets);
        for (uint256 i = 0; i < rewardControllers.length;) { 
            rewardControllers[i].claimReward(address(this), owner);
            unchecked { ++i; }
        }
    }

    /** @notice See {IHATVault-deposit}. */
    function deposit(uint256 assets, address receiver, uint256 minShares) external virtual returns (uint256) {
        uint256 shares = deposit(assets, receiver);
        if (shares < minShares) revert DepositSlippageProtection();
        return shares;
    }

    /** @notice See {IHATVault-mint}. */
    function mint(uint256 shares, address receiver, uint256 maxAssets) external virtual returns (uint256) {
        uint256 assets = mint(shares, receiver);
        if (assets > maxAssets) revert MintSlippageProtection();
        return assets;
    }

    /** @notice See {IERC4626Upgradeable-maxDeposit}. */
    function maxDeposit(address) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256) {
        return depositPause ? 0 : MAX_UINT;
    }

    /** @notice See {IERC4626Upgradeable-maxMint}. */
    function maxMint(address) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256) {
        return depositPause ? 0 : MAX_UINT;
    }

    /** @notice See {IERC4626Upgradeable-maxWithdraw}. */
    function maxWithdraw(address owner) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256) {
        if (activeClaim.createdAt != 0 || !_isWithdrawEnabledForUser(owner)) return 0;
        return previewRedeem(balanceOf(owner));
    }

    /** @notice See {IERC4626Upgradeable-maxRedeem}. */
    function maxRedeem(address owner) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256) {
        if (activeClaim.createdAt != 0 || !_isWithdrawEnabledForUser(owner)) return 0;
        return balanceOf(owner);
    }

    /** @notice See {IERC4626Upgradeable-previewWithdraw}. */
    function previewWithdraw(uint256 assets) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256 shares) {
        (shares,) = previewWithdrawAndFee(assets);
    }

    /** @notice See {IERC4626Upgradeable-previewRedeem}. */
    function previewRedeem(uint256 shares) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256 assets) {
        (assets,) = previewRedeemAndFee(shares);
    }

    /** @notice See {IHATVault-previewWithdrawAndFee}. */
    function previewWithdrawAndFee(uint256 assets) public view returns (uint256 shares, uint256 fee) {
        uint256 _withdrawalFee = withdrawalFee;
        fee = assets.mulDiv(_withdrawalFee, (HUNDRED_PERCENT - _withdrawalFee));
        shares = _convertToShares(assets + fee, MathUpgradeable.Rounding.Up);
    }

    /** @notice See {IHATVault-previewRedeemAndFee}. */
    function previewRedeemAndFee(uint256 shares) public view returns (uint256 assets, uint256 fee) {
        uint256 _assetsPlusFee = _convertToAssets(shares, MathUpgradeable.Rounding.Down);
        fee = _assetsPlusFee.mulDiv(withdrawalFee, HUNDRED_PERCENT);
        unchecked { // fee will always be maximun 20% of _assetsPlusFee
            assets = _assetsPlusFee - fee;
        }
    }

    /* -------------------------------------------------------------------------------- */

    /* --------------------------------- Getters -------------------------------------- */

    /** @notice See {IHATVault-getBountyGovernanceHAT}. */
    function getBountyGovernanceHAT() public view returns(uint16) {
        uint16 _bountyGovernanceHAT = bountyGovernanceHAT;
        if (_bountyGovernanceHAT != NULL_UINT16) {
            return _bountyGovernanceHAT;
        } else {
            return registry.defaultBountyGovernanceHAT();
        }
    }

    /** @notice See {IHATVault-getBountyHackerHATVested}. */
    function getBountyHackerHATVested() public view returns(uint16) {
        uint16 _bountyHackerHATVested = bountyHackerHATVested;
        if (_bountyHackerHATVested != NULL_UINT16) {
            return _bountyHackerHATVested;
        } else {
            return registry.defaultBountyHackerHATVested();
        }
    }

    /** @notice See {IHATVault-getArbitrator}. */
    function getArbitrator() public view returns(address) {
        address _arbitrator = arbitrator;
        if (_arbitrator != NULL_ADDRESS) {
            return _arbitrator;
        } else {
            return registry.defaultArbitrator();
        }
    }

    /** @notice See {IHATVault-getChallengePeriod}. */
    function getChallengePeriod() public view returns(uint32) {
        uint32 _challengePeriod = challengePeriod;
        if (_challengePeriod != NULL_UINT32) {
            return _challengePeriod;
        } else {
            return registry.defaultChallengePeriod();
        }
    }

    /** @notice See {IHATVault-getChallengeTimeOutPeriod}. */
    function getChallengeTimeOutPeriod() public view returns(uint32) {
        uint32 _challengeTimeOutPeriod = challengeTimeOutPeriod;
        if (_challengeTimeOutPeriod != NULL_UINT32) {
            return _challengeTimeOutPeriod;
        } else {
            return registry.defaultChallengeTimeOutPeriod();
        }
    }

    /** @notice See {IHATVault-getArbitratorCanChangeBounty}. */
    function getArbitratorCanChangeBounty() public view returns(bool) {
        ArbitratorCanChangeBounty _arbitratorCanChangeBounty = arbitratorCanChangeBounty;
        if (_arbitratorCanChangeBounty != ArbitratorCanChangeBounty.DEFAULT) {
            return _arbitratorCanChangeBounty == ArbitratorCanChangeBounty.YES;
        } else {
            return registry.defaultArbitratorCanChangeBounty();
        }
    }

    /* -------------------------------------------------------------------------------- */

    /* --------------------------------- Helpers -------------------------------------- */

    /**
    * @dev Deposit funds to the vault. Can only be called if the committee had
    * checked in and deposits are not paused.
    * @param caller Caller of the action (msg.sender)
    * @param receiver Reciever of the shares from the deposit
    * @param assets Amount of vault's native token to deposit
    * @param shares Respective amount of shares to be received
    */
    function _deposit(
        address caller,
        address receiver,
        uint256 assets,
        uint256 shares
    ) internal override virtual nonReentrant {
        if (!committeeCheckedIn)
            revert CommitteeNotCheckedInYet();
        if (receiver == caller && withdrawEnableStartTime[receiver] != 0 ) {
            // clear withdraw request if caller deposits in her own account
            withdrawEnableStartTime[receiver] = 0;
        }

        super._deposit(caller, receiver, assets, shares);
    }

    // amount of shares correspond with assets + fee
    function _withdraw(
        address _caller,
        address _receiver,
        address _owner,
        uint256 _assets,
        uint256 _shares,
        uint256 _fee
    ) internal nonReentrant {
        if (_assets == 0) revert WithdrawMustBeGreaterThanZero();
        if (_caller != _owner) {
            _spendAllowance(_owner, _caller, _shares);
        }

        _burn(_owner, _shares);

        IERC20 _asset = IERC20(asset());
        if (_fee > 0) {
            _asset.safeTransfer(registry.owner(), _fee);
        }
        _asset.safeTransfer(_receiver, _assets);

        emit Withdraw(_caller, _receiver, _owner, _assets, _shares);
    }

    function _beforeTokenTransfer(
        address _from,
        address _to,
        uint256 _amount
    ) internal virtual override {
        if (_amount == 0) revert AmountCannotBeZero();
        if (_from == _to) revert CannotTransferToSelf();
        // deposit/mint/transfer
        if (_to != address(0)) {
            HATVaultsRegistry  _registry = registry;
            if (_registry.isEmergencyPaused()) revert SystemInEmergencyPause();
            // Cannot transfer or mint tokens to a user for which an active withdraw request exists
            // because then we would need to reset their withdraw request
            uint256 _withdrawEnableStartTime = withdrawEnableStartTime[_to];
            if (_withdrawEnableStartTime != 0) {
                // solhint-disable-next-line not-rely-on-time
                if (block.timestamp <= _withdrawEnableStartTime + _registry.getWithdrawRequestEnablePeriod())
                    revert CannotTransferToAnotherUserWithActiveWithdrawRequest();
            }

            for (uint256 i = 0; i < rewardControllers.length;) { 
                rewardControllers[i].commitUserBalance(_to, _amount, true);
                unchecked { ++i; }
            }
        }
        // withdraw/redeem/transfer
        if (_from != address(0)) {
            if (_amount > maxRedeem(_from)) revert RedeemMoreThanMax();
            // if all is ok and withdrawal can be made - 
            // reset withdrawRequests[_pid][msg.sender] so that another withdrawRequest
            // will have to be made before next withdrawal
            withdrawEnableStartTime[_from] = 0;

            if (!_isEmergencyWithdraw) {
                for (uint256 i = 0; i < rewardControllers.length;) { 
                    rewardControllers[i].commitUserBalance(_from, _amount, false);
                    unchecked { ++i; }
                }
            }
        }
    }

    function _afterTokenTransfer(address, address, uint256) internal virtual override {
        if (totalSupply() > 0 && totalSupply() < MINIMAL_AMOUNT_OF_SHARES) {
          revert AmountOfSharesMustBeMoreThanMinimalAmount();
        }
    }

    function _setVestingParams(uint32 _duration, uint32 _periods) internal {
        if (_duration > 120 days) revert VestingDurationTooLong();
        if (_periods == 0) revert VestingPeriodsCannotBeZero();
        if (_duration < _periods) revert VestingDurationSmallerThanPeriods();
        vestingDuration = _duration;
        vestingPeriods = _periods;
        emit SetVestingParams(_duration, _periods);
    }

    /**
    * @dev Checks that the given user can perform a withdraw at this time
    * @param _user Address of the user to check
    */
    function _isWithdrawEnabledForUser(address _user)
        internal view
        returns(bool)
    {
        HATVaultsRegistry _registry = registry;
        uint256 _withdrawPeriod = _registry.getWithdrawPeriod();
        // disable withdraw for safetyPeriod (e.g 1 hour) after each withdrawPeriod (e.g 11 hours)
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp % (_withdrawPeriod + _registry.getSafetyPeriod()) >= _withdrawPeriod)
            return false;
        // check that withdrawRequestPendingPeriod had passed
        uint256 _withdrawEnableStartTime = withdrawEnableStartTime[_user];
        // solhint-disable-next-line not-rely-on-time
        return (block.timestamp >= _withdrawEnableStartTime &&
        // check that withdrawRequestEnablePeriod had not passed and that the
        // last action was withdrawRequest (and not deposit or withdraw, which
        // reset withdrawRequests[_user] to 0)
        // solhint-disable-next-line not-rely-on-time
            block.timestamp <= _withdrawEnableStartTime + _registry.getWithdrawRequestEnablePeriod());
    }

    /**
    * @dev calculate the specific bounty payout distribution, according to the
    * predefined bounty split and the given bounty percentage
    * @param _bountyPercentage The percentage of the vault's funds to be paid
    * out as bounty
    * @param _bountyGovernanceHAT The bountyGovernanceHAT at the time the claim was submitted
    * @param _bountyHackerHATVested The bountyHackerHATVested at the time the claim was submitted
    * @return claimBounty The bounty distribution for this specific claim
    */
    function _calcClaimBounty(
        uint256 _bountyPercentage,
        uint256 _bountyGovernanceHAT,
        uint256 _bountyHackerHATVested
    ) internal view returns(IHATVault.ClaimBounty memory claimBounty) {
        uint256 _totalAssets = totalAssets();
        if (_totalAssets == 0) {
          return claimBounty;
        }
        if (_bountyPercentage > maxBounty)
            revert BountyPercentageHigherThanMaxBounty();

        uint256 _totalBountyAmount = _totalAssets * _bountyPercentage;

        uint256 _governanceHatAmount = _totalBountyAmount.mulDiv(_bountyGovernanceHAT, HUNDRED_PERCENT_SQRD);
        uint256 _hackerHatVestedAmount = _totalBountyAmount.mulDiv(_bountyHackerHATVested, HUNDRED_PERCENT_SQRD);

        _totalBountyAmount -= (_governanceHatAmount + _hackerHatVestedAmount) * HUNDRED_PERCENT;

        claimBounty.governanceHat = _governanceHatAmount;
        claimBounty.hackerHatVested = _hackerHatVestedAmount;

        uint256 _hackerVestedAmount = _totalBountyAmount.mulDiv(bountySplit.hackerVested, HUNDRED_PERCENT_SQRD);
        uint256 _hackerAmount = _totalBountyAmount.mulDiv(bountySplit.hacker, HUNDRED_PERCENT_SQRD);

        _totalBountyAmount -= (_hackerVestedAmount + _hackerAmount) * HUNDRED_PERCENT;

        claimBounty.hackerVested = _hackerVestedAmount;
        claimBounty.hacker = _hackerAmount;

        // give all the tokens left to the committee to avoid rounding errors
        claimBounty.committee = _totalBountyAmount / HUNDRED_PERCENT;
    }

    /** 
    * @dev Check that a given bounty split is legal, meaning that:
    *   Each entry is a number between 0 and `HUNDRED_PERCENT`.
    *   Except committee part which is capped at maximum of
    *   `MAX_COMMITTEE_BOUNTY`.
    *   Total splits should be equal to `HUNDRED_PERCENT`.
    * function will revert in case the bounty split is not legal.
    * @param _bountySplit The bounty split to check
    */
    function _validateSplit(IHATVault.BountySplit calldata _bountySplit) internal pure {
        if (_bountySplit.committee > MAX_COMMITTEE_BOUNTY) revert CommitteeBountyCannotBeMoreThanMax();
        if (_bountySplit.hackerVested +
            _bountySplit.hacker +
            _bountySplit.committee != HUNDRED_PERCENT)
            revert TotalSplitPercentageShouldBeHundredPercent();
    }

    /* -------------------------------------------------------------------------------- */
}

File 24 of 29 : IHATVault.sol
// SPDX-License-Identifier: MIT
// Disclaimer https://github.com/hats-finance/hats-contracts/blob/main/DISCLAIMER.md

pragma solidity 0.8.16;

import "./IRewardController.sol";
import "@openzeppelin/contracts-upgradeable/interfaces/IERC4626Upgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/** @title Interface for Hats.finance Vaults
 * @author Hats.finance
 * @notice A HATVault holds the funds for a specific project's bug bounties.
 * Anyone can permissionlessly deposit into the HATVault using
 * the vault’s native token. When a bug is submitted and approved, the bounty 
 * is paid out using the funds in the vault. Bounties are paid out as a
 * percentage of the vault. The percentage is set according to the severity of
 * the bug. Vaults have regular safety periods (typically for an hour twice a
 * day) which are time for the committee to make decisions.
 *
 * In addition to the roles defined in the HATVaultsRegistry, every HATVault 
 * has the roles:
 * Committee - The only address which can submit a claim for a bounty payout
 * and set the maximum bounty.
 * User - Anyone can deposit the vault's native token into the vault and 
 * recieve shares for it. Shares represent the user's relative part in the
 * vault, and when a bounty is paid out, users lose part of their deposits
 * (based on percentage paid), but keep their share of the vault.
 * Users also receive rewards for their deposits, which can be claimed at any
 *  time.
 * To withdraw previously deposited tokens, a user must first send a withdraw
 * request, and the withdrawal will be made available after a pending period.
 * Withdrawals are not permitted during safety periods or while there is an 
 * active claim for a bounty payout.
 *
 * Bounties are payed out distributed between a few channels, and that 
 * distribution is set upon creation (the hacker gets part in direct transfer,
 * part in vested reward and part in vested HAT token, part gets rewarded to
 * the committee, part gets swapped to HAT token and burned and/or sent to Hats
 * governance).
 *
 * NOTE: Vaults should not use tokens which do not guarantee that the amount
 * specified is the amount transferred
 *
 * This project is open-source and can be found at:
 * https://github.com/hats-finance/hats-contracts
 */
interface IHATVault is IERC4626Upgradeable {

    enum ArbitratorCanChangeBounty{ NO, YES, DEFAULT }

    // How to divide the bounty - after deducting the part that is swapped to
    // HAT tokens (and then sent to governance and vested to the hacker)
    // values are in percentages and should add up to 100% (defined as 10000)
    struct BountySplit {
        // the percentage of reward sent to the hacker via vesting contract
        uint16 hackerVested;
        // the percentage of tokens that are sent directly to the hacker
        uint16 hacker;
        // the percentage sent to the committee
        uint16 committee;
    }

    // How to divide a bounty for a claim that has been approved
    // used to keep track of payouts, amounts are in vault's native token
    struct ClaimBounty {
        uint256 hacker;
        uint256 hackerVested;
        uint256 committee;
        uint256 hackerHatVested;
        uint256 governanceHat;
    }

    /**
    * @notice Initialization parameters for the vault
    * @param name The vault's name (concatenated as "Hats Vault " + name)
    * @param symbol The vault's symbol (concatenated as "HAT" + symbol)
    * @param rewardController The reward controller for the vault
    * @param vestingDuration Duration of the vesting period of the vault's
    * token vested part of the bounty
    * @param vestingPeriods The number of vesting periods of the vault's token
    * vested part of the bounty
    * @param maxBounty The maximum percentage of the vault that can be paid
    * out as a bounty
    * @param bountySplit The way to split the bounty between the hacker, 
    * hacker vested, and committee.
    *   Each entry is a number between 0 and `HUNDRED_PERCENT`.
    *   Total splits should be equal to `HUNDRED_PERCENT`.
    * @param asset The vault's native token
    * @param owner The address of the vault's owner 
    * @param committee The address of the vault's committee 
    * @param isPaused Whether to initialize the vault with deposits disabled
    * @dev Needed to avoid a "stack too deep" error
    */
    struct VaultInitParams {
        string name;
        string symbol;
        IRewardController[] rewardControllers;
        uint32 vestingDuration;
        uint32 vestingPeriods;
        uint16 maxBounty;
        IHATVault.BountySplit bountySplit;
        IERC20 asset;
        address owner;
        address committee;
        bool isPaused;
        string descriptionHash;
    }

    // Only committee
    error OnlyCommittee();
    // Active claim exists
    error ActiveClaimExists();
    // Safety period
    error SafetyPeriod();
    // Not safety period
    error NotSafetyPeriod();
    // Bounty percentage is higher than the max bounty
    error BountyPercentageHigherThanMaxBounty();
    // Only callable by arbitrator or after challenge timeout period
    error OnlyCallableByArbitratorOrAfterChallengeTimeOutPeriod();
    // No active claim exists
    error NoActiveClaimExists();
    // Claim Id specified is not the active claim Id
    error ClaimIdIsNotActive();
    // Not enough fee paid
    error NotEnoughFeePaid();
    // No pending max bounty
    error NoPendingMaxBounty();
    // Delay period for setting max bounty had not passed
    error DelayPeriodForSettingMaxBountyHadNotPassed();
    // Committee already checked in
    error CommitteeAlreadyCheckedIn();
    // Total bounty split % should be `HUNDRED_PERCENT`
    error TotalSplitPercentageShouldBeHundredPercent();
    // Vesting duration is too long
    error VestingDurationTooLong();
    // Vesting periods cannot be zero
    error VestingPeriodsCannotBeZero();
    // Vesting duration smaller than periods
    error VestingDurationSmallerThanPeriods();
    // Max bounty cannot be more than `MAX_BOUNTY_LIMIT`
    error MaxBountyCannotBeMoreThanMaxBountyLimit();
    // Committee bounty split cannot be more than `MAX_COMMITTEE_BOUNTY`
    error CommitteeBountyCannotBeMoreThanMax();
    // Only registry owner
    error OnlyRegistryOwner();
    // Only fee setter
    error OnlyFeeSetter();
    // Fee must be less than or equal to 2%
    error WithdrawalFeeTooBig();
    // Set shares arrays must have same length
    error SetSharesArraysMustHaveSameLength();
    // Committee not checked in yet
    error CommitteeNotCheckedInYet();
    // Not enough user balance
    error NotEnoughUserBalance();
    // Only arbitrator or registry owner
    error OnlyArbitratorOrRegistryOwner();
    // Unchalleged claim can only be approved if challenge period is over
    error UnchallengedClaimCanOnlyBeApprovedAfterChallengePeriod();
    // Challenged claim can only be approved by arbitrator before the challenge timeout period
    error ChallengedClaimCanOnlyBeApprovedByArbitratorUntilChallengeTimeoutPeriod();
    // Claim has expired
    error ClaimExpired();
    // Challenge period is over
    error ChallengePeriodEnded();
    // Claim can be challenged only once
    error ClaimAlreadyChallenged();
    // Only callable if challenged
    error OnlyCallableIfChallenged();
    // Cannot deposit to another user with withdraw request
    error CannotTransferToAnotherUserWithActiveWithdrawRequest();
    // Withdraw amount must be greater than zero
    error WithdrawMustBeGreaterThanZero();
    // Redeem amount cannot be more than maximum for user
    error RedeemMoreThanMax();
    // System is in an emergency pause
    error SystemInEmergencyPause();
    // Cannot set a reward controller that was already used in the past
    error CannotSetToPerviousRewardController();
    // Cannot mint burn or transfer 0 amount of shares
    error AmountCannotBeZero();
    // Cannot transfer shares to self
    error CannotTransferToSelf();
    // First deposit must return at least MINIMAL_AMOUNT_OF_SHARES
    error AmountOfSharesMustBeMoreThanMinimalAmount();
    // Deposit passed max slippage
    error DepositSlippageProtection();
    // Mint passed max slippage
    error MintSlippageProtection();
    // Withdraw passed max slippage
    error WithdrawSlippageProtection();
    // Redeem passed max slippage
    error RedeemSlippageProtection();
    // Cannot add the same reward controller more than once
    error DuplicatedRewardController();


    event SubmitClaim(
        bytes32 indexed _claimId,
        address indexed _committee,
        address indexed _beneficiary,
        uint256 _bountyPercentage,
        string _descriptionHash
    );
    event ChallengeClaim(bytes32 indexed _claimId);
    event ApproveClaim(
        bytes32 indexed _claimId,
        address indexed _committee,
        address indexed _beneficiary,
        uint256 _bountyPercentage,
        address _tokenLock,
        ClaimBounty _claimBounty
    );
    event DismissClaim(bytes32 indexed _claimId);
    event SetCommittee(address indexed _committee);
    event SetVestingParams(
        uint256 _duration,
        uint256 _periods
    );
    event SetBountySplit(BountySplit _bountySplit);
    event SetWithdrawalFee(uint256 _newFee);
    event CommitteeCheckedIn();
    event SetPendingMaxBounty(uint256 _maxBounty);
    event SetMaxBounty(uint256 _maxBounty);
    event AddRewardController(IRewardController indexed _newRewardController);
    event SetDepositPause(bool _depositPause);
    event SetVaultDescription(string _descriptionHash);
    event SetHATBountySplit(uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested);
    event SetArbitrator(address indexed _arbitrator);
    event SetChallengePeriod(uint256 _challengePeriod);
    event SetChallengeTimeOutPeriod(uint256 _challengeTimeOutPeriod);
    event SetArbitratorCanChangeBounty(ArbitratorCanChangeBounty _arbitratorCanChangeBounty);
    event WithdrawRequest(
        address indexed _beneficiary,
        uint256 _withdrawEnableTime
    );

    /**
    * @notice Initialize a vault instance
    * @param _params The vault initialization parameters
    * @dev See {IHATVault-VaultInitParams} for more details
    * @dev Called when the vault is created in {IHATVaultsRegistry-createVault}
    */
    function initialize(VaultInitParams calldata _params) external;

    /* -------------------------------------------------------------------------------- */

    /* ---------------------------------- Claim --------------------------------------- */

    /**
     * @notice Called by the committee to submit a claim for a bounty payout.
     * This function should be called only on a safety period, when withdrawals
     * are disabled, and while there's no other active claim. Cannot be called
     * when the registry is in an emergency pause.
     * Upon a call to this function by the committee the vault's withdrawals
     * will be disabled until the claim is approved or dismissed. Also from the
     * time of this call the arbitrator will have a period of 
     * {HATVaultsRegistry.challengePeriod} to challenge the claim.
     * @param _beneficiary The submitted claim's beneficiary
     * @param _bountyPercentage The submitted claim's bug requested reward percentage
     */
    function submitClaim(
        address _beneficiary, 
        uint16 _bountyPercentage, 
        string calldata _descriptionHash
    )
        external
        returns (bytes32 claimId);

   
    /**
    * @notice Called by the arbitrator or governance to challenge a claim for a bounty
    * payout that had been previously submitted by the committee.
    * Can only be called during the challenge period after submission of the
    * claim.
    * @param _claimId The claim ID
    */
    function challengeClaim(bytes32 _claimId) external;

    /**
    * @notice Approve a claim for a bounty submitted by a committee, and
    * pay out bounty to hacker and committee. Also transfer to the 
    * HATVaultsRegistry the part of the bounty that will be swapped to HAT 
    * tokens.
    * If the claim had been previously challenged, this is only callable by
    * the arbitrator. Otherwise, callable by anyone after challengePeriod had
    * passed.
    * @param _claimId The claim ID
    * @param _bountyPercentage The percentage of the vault's balance that will
    * be sent as a bounty. This value will be ignored if the caller is not the
    * arbitrator.
    */
    function approveClaim(bytes32 _claimId, uint16 _bountyPercentage)
        external;

    /**
    * @notice Dismiss the active claim for bounty payout submitted by the
    * committee.
    * Called either by the arbitrator, or by anyone if the claim has timed out.
    * @param _claimId The claim ID
    */
    function dismissClaim(bytes32 _claimId) external;

    /* -------------------------------------------------------------------------------- */

    /* ---------------------------------- Params -------------------------------------- */

    /**
    * @notice Set new committee address. Can be called by existing committee,
    * or by the the vault's owner in the case that the committee hadn't checked in
    * yet.
    * @param _committee The address of the new committee 
    */
    function setCommittee(address _committee) external;

    /**
    * @notice Called by the vault's owner to set the vesting params for the
    * part of the bounty that the hacker gets vested in the vault's native
    * token
    * @param _duration Duration of the vesting period. Must be smaller than
    * 120 days and bigger than `_periods`
    * @param _periods Number of vesting periods. Cannot be 0.
    */
    function setVestingParams(uint32 _duration, uint32 _periods) external;

    /**
    * @notice Called by the vault's owner to set the vault token bounty split
    * upon an approval.
    * Can only be called if is no active claim and not during safety periods.
    * @param _bountySplit The bounty split
    */
    function setBountySplit(BountySplit calldata _bountySplit) external;

    /**
    * @notice Called by the registry's fee setter to set the fee for 
    * withdrawals from the vault.
    * @param _fee The new fee. Must be smaller than or equal to `MAX_WITHDRAWAL_FEE`
    */
    function setWithdrawalFee(uint256 _fee) external;

    /**
    * @notice Called by the vault's committee to claim it's role.
    * Deposits are enabled only after committee check in.
    */
    function committeeCheckIn() external;

    /**
    * @notice Called by the vault's owner to set a pending request for the
    * maximum percentage of the vault that can be paid out as a bounty.
    * Cannot be called if there is an active claim that has been submitted.
    * Max bounty should be less than or equal to 90% (defined as 9000).
    * The pending value can be set by the owner after the time delay (of 
    * {HATVaultsRegistry.generalParameters.setMaxBountyDelay}) had passed.
    * @param _maxBounty The maximum bounty percentage that can be paid out
    */
    function setPendingMaxBounty(uint16 _maxBounty) external;

    /**
    * @notice Called by the vault's owner to set the vault's max bounty to
    * the already pending max bounty.
    * Cannot be called if there are active claims that have been submitted.
    * Can only be called if there is a max bounty pending approval, and the
    * time delay since setting the pending max bounty had passed.
    */
    function setMaxBounty() external;

    /**
    * @notice Called by the vault's owner to disable all deposits to the vault
    * @param _depositPause Are deposits paused
    */
    function setDepositPause(bool _depositPause) external;

    /**
    * @notice Called by the registry's owner to change the description of the
    * vault in the Hats.finance UI
    * @param _descriptionHash the hash of the vault's description
    */
    function setVaultDescription(string calldata _descriptionHash) external;

    /**
    * @notice Called by the registry's owner to add a reward controller to the vault
    * @param _newRewardController The new reward controller to add
    */
    function addRewardController(IRewardController _newRewardController) external;

    /**
    * @notice Called by the registry's owner to set the vault HAT token bounty 
    * split upon an approval.
    * If the value passed is the special "null" value the vault will use the
    * registry's default value.
    * @param _bountyGovernanceHAT The HAT bounty for governance
    * @param _bountyHackerHATVested The HAT bounty vested for the hacker
    */
    function setHATBountySplit(
        uint16 _bountyGovernanceHAT,
        uint16 _bountyHackerHATVested
    ) 
        external;

    /**
    * @notice Called by the registry's owner to set the vault arbitrator
    * If the value passed is the special "null" value the vault will use the
    * registry's default value.
    * @param _arbitrator The address of vault's arbitrator
    */
    function setArbitrator(address _arbitrator) external;

    /**
    * @notice Called by the registry's owner to set the period of time after
    * a claim for a bounty payout has been submitted that it can be challenged
    * by the arbitrator.
    * If the value passed is the special "null" value the vault will use the
    * registry's default value.
    * @param _challengePeriod The vault's challenge period
    */
    function setChallengePeriod(uint32 _challengePeriod) external;

    /**
    * @notice Called by the registry's owner to set the period of time after
    * which a claim for a bounty payout can be dismissed by anyone.
    * If the value passed is the special "null" value the vault will use the
    * registry's default value.
    * @param _challengeTimeOutPeriod The vault's challenge timeout period
    */
    function setChallengeTimeOutPeriod(uint32 _challengeTimeOutPeriod)
        external;

    /**
    * @notice Called by the registry's owner to set whether the arbitrator
    * can change a claim bounty percentage
    * If the value passed is the special "null" value the vault will use the
    * registry's default value.
    * @param _arbitratorCanChangeBounty Whether the arbitrator can change a claim bounty percentage
    */
    function setArbitratorCanChangeBounty(ArbitratorCanChangeBounty _arbitratorCanChangeBounty) external;

    /* -------------------------------------------------------------------------------- */

    /* ---------------------------------- Vault --------------------------------------- */

    /**
    * @notice Submit a request to withdraw funds from the vault.
    * The request will only be approved if there is no previous active
    * withdraw request.
    * The request will be pending for a period of
    * {HATVaultsRegistry.generalParameters.withdrawRequestPendingPeriod},
    * after which a withdraw will be possible for a duration of
    * {HATVaultsRegistry.generalParameters.withdrawRequestEnablePeriod}
    */
    function withdrawRequest() external;

    /** 
    * @notice Withdraw previously deposited funds from the vault and claim
    * the HAT reward that the user has earned.
    * Can only be performed if a withdraw request has been previously
    * submitted, and the pending period had passed, and while the withdraw
    * enabled timeout had not passed. Withdrawals are not permitted during
    * safety periods or while there is an active claim for a bounty payout.
    * @param assets Amount of tokens to withdraw
    * @param receiver Address of receiver of the funds
    * @param owner Address of owner of the funds
    * @dev See {IERC4626-withdraw}.
    */
    function withdrawAndClaim(uint256 assets, address receiver, address owner)
        external 
        returns (uint256 shares);

    /** 
    * @notice Redeem shares in the vault for the respective amount
    * of underlying assets and claim the HAT reward that the user has earned.
    * Can only be performed if a withdraw request has been previously
    * submitted, and the pending period had passed, and while the withdraw
    * enabled timeout had not passed. Withdrawals are not permitted during
    * safety periods or while there is an active claim for a bounty payout.
    * @param shares Amount of shares to redeem
    * @param receiver Address of receiver of the funds 
    * @param owner Address of owner of the funds 
    * @dev See {IERC4626-redeem}.
    */
    function redeemAndClaim(uint256 shares, address receiver, address owner)
        external 
        returns (uint256 assets);

    /** 
    * @notice Redeem all of the user's shares in the vault for the respective amount
    * of underlying assets without calling the reward controller, meaning user renounces
    * their uncommited part of the reward.
    * Can only be performed if a withdraw request has been previously
    * submitted, and the pending period had passed, and while the withdraw
    * enabled timeout had not passed. Withdrawals are not permitted during
    * safety periods or while there is an active claim for a bounty payout.
    * @param receiver Address of receiver of the funds 
    */
    function emergencyWithdraw(address receiver) external returns (uint256 assets);

    /** 
    * @notice Withdraw previously deposited funds from the vault, without
    * transferring the accumulated rewards.
    * Can only be performed if a withdraw request has been previously
    * submitted, and the pending period had passed, and while the withdraw
    * enabled timeout had not passed. Withdrawals are not permitted during
    * safety periods or while there is an active claim for a bounty payout.
    * @param assets Amount of tokens to withdraw
    * @param receiver Address of receiver of the funds 
    * @param owner Address of owner of the funds 
    * @dev See {IERC4626-withdraw}.
    */
    function withdraw(uint256 assets, address receiver, address owner)
        external 
        returns (uint256);

    /** 
    * @notice Redeem shares in the vault for the respective amount
    * of underlying assets, without transferring the accumulated reward.
    * Can only be performed if a withdraw request has been previously
    * submitted, and the pending period had passed, and while the withdraw
    * enabled timeout had not passed. Withdrawals are not permitted during
    * safety periods or while there is an active claim for a bounty payout.
    * @param shares Amount of shares to redeem
    * @param receiver Address of receiver of the funds 
    * @param owner Address of owner of the funds 
    * @dev See {IERC4626-redeem}.
    */
    function redeem(uint256 shares, address receiver, address owner)
        external  
        returns (uint256);

    /**
    * @dev Deposit funds to the vault. Can only be called if the committee had
    * checked in and deposits are not paused, and the registry is not in an emergency pause.
    * @param receiver Reciever of the shares from the deposit
    * @param assets Amount of vault's native token to deposit
    * @dev See {IERC4626-deposit}.
    */
    function deposit(uint256 assets, address receiver) 
        external
        returns (uint256);

    /**
    * @dev Deposit funds to the vault. Can only be called if the committee had
    * checked in and deposits are not paused, and the registry is not in an emergency pause.
    * Allows to specify minimum shares to be minted for slippage protection.
    * @param receiver Reciever of the shares from the deposit
    * @param assets Amount of vault's native token to deposit
    * @param minShares Minimum amount of shares to minted for the assets
    */
    function deposit(uint256 assets, address receiver, uint256 minShares) 
        external
        returns (uint256);

    /**
    * @dev Deposit funds to the vault based on the amount of shares to mint specified.
    * Can only be called if the committee had checked in and deposits are not paused,
    * and the registry is not in an emergency pause.
    * Allows to specify maximum assets to be deposited for slippage protection.
    * @param receiver Reciever of the shares from the deposit
    * @param shares Amount of vault's shares to mint
    * @param maxAssets Maximum amount of assets to deposit for the shares
    */
    function mint(uint256 shares, address receiver, uint256 maxAssets) 
        external
        returns (uint256);

    /** 
    * @notice Withdraw previously deposited funds from the vault, without
    * transferring the accumulated HAT reward.
    * Can only be performed if a withdraw request has been previously
    * submitted, and the pending period had passed, and while the withdraw
    * enabled timeout had not passed. Withdrawals are not permitted during
    * safety periods or while there is an active claim for a bounty payout.
    * Allows to specify maximum shares to be burnt for slippage protection.
    * @param assets Amount of tokens to withdraw
    * @param receiver Address of receiver of the funds 
    * @param owner Address of owner of the funds
    * @param maxShares Maximum amount of shares to burn for the assets
    */
    function withdraw(uint256 assets, address receiver, address owner, uint256 maxShares)
        external 
        returns (uint256);

    /** 
    * @notice Redeem shares in the vault for the respective amount
    * of underlying assets, without transferring the accumulated reward.
    * Can only be performed if a withdraw request has been previously
    * submitted, and the pending period had passed, and while the withdraw
    * enabled timeout had not passed. Withdrawals are not permitted during
    * safety periods or while there is an active claim for a bounty payout.
    * Allows to specify minimum assets to be received for slippage protection.
    * @param shares Amount of shares to redeem
    * @param receiver Address of receiver of the funds 
    * @param owner Address of owner of the funds
    * @param minAssets Minimum amount of assets to receive for the shares
    */
    function redeem(uint256 shares, address receiver, address owner, uint256 minAssets)
        external  
        returns (uint256);

    /** 
    * @notice Withdraw previously deposited funds from the vault and claim
    * the HAT reward that the user has earned.
    * Can only be performed if a withdraw request has been previously
    * submitted, and the pending period had passed, and while the withdraw
    * enabled timeout had not passed. Withdrawals are not permitted during
    * safety periods or while there is an active claim for a bounty payout.
    * Allows to specify maximum shares to be burnt for slippage protection.
    * @param assets Amount of tokens to withdraw
    * @param receiver Address of receiver of the funds
    * @param owner Address of owner of the funds
    * @param maxShares Maximum amount of shares to burn for the assets
    * @dev See {IERC4626-withdraw}.
    */
    function withdrawAndClaim(uint256 assets, address receiver, address owner, uint256 maxShares)
        external 
        returns (uint256 shares);

    /** 
    * @notice Redeem shares in the vault for the respective amount
    * of underlying assets and claim the HAT reward that the user has earned.
    * Can only be performed if a withdraw request has been previously
    * submitted, and the pending period had passed, and while the withdraw
    * enabled timeout had not passed. Withdrawals are not permitted during
    * safety periods or while there is an active claim for a bounty payout.
    * Allows to specify minimum assets to be received for slippage protection.
    * @param shares Amount of shares to redeem
    * @param receiver Address of receiver of the funds 
    * @param owner Address of owner of the funds
    * @param minAssets Minimum amount of assets to receive for the shares
    * @dev See {IERC4626-redeem}.
    */
    function redeemAndClaim(uint256 shares, address receiver, address owner, uint256 minAssets)
        external 
        returns (uint256 assets);

    /* -------------------------------------------------------------------------------- */

    /* --------------------------------- Getters -------------------------------------- */

    /** 
    * @notice Returns the vault HAT bounty split part that goes to the governance
    * If no specific value for this vault has been set, the registry's default
    * value will be returned.
    * @return The vault's HAT bounty split part that goes to the governance
    */
    function getBountyGovernanceHAT() external view returns(uint16);
    
    /** 
    * @notice Returns the vault HAT bounty split part that is vested for the hacker
    * If no specific value for this vault has been set, the registry's default
    * value will be returned.
    * @return The vault's HAT bounty split part that is vested for the hacker
    */
    function getBountyHackerHATVested() external view returns(uint16);

    /** 
    * @notice Returns the address of the vault's arbitrator
    * If no specific value for this vault has been set, the registry's default
    * value will be returned.
    * @return The address of the vault's arbitrator
    */
    function getArbitrator() external view returns(address);

    /** 
    * @notice Returns the period of time after a claim for a bounty payout has
    * been submitted that it can be challenged by the arbitrator.
    * If no specific value for this vault has been set, the registry's default
    * value will be returned.
    * @return The vault's challenge period
    */
    function getChallengePeriod() external view returns(uint32);

    /** 
    * @notice Returns the period of time after which a claim for a bounty
    * payout can be dismissed by anyone.
    * If no specific value for this vault has been set, the registry's default
    * value will be returned.
    * @return The vault's challenge timeout period
    */
    function getChallengeTimeOutPeriod() external view returns(uint32);

    /** 
    * @notice Returns the amount of shares to be burned to give the user the exact
    * amount of assets requested plus cover for the fee. Also returns the amount assets
    * to be paid as fee.
    * @return shares The amount of shares to be burned to get the requested amount of assets
    * @return fee The amount of assets that will be paid as fee
    */
    function previewWithdrawAndFee(uint256 assets) external view returns (uint256 shares, uint256 fee);


    /** 
    * @notice Returns the amount of assets to be sent to the user for the exact
    * amount of shares to redeem. Also returns the amount assets to be paid as fee.
    * @return assets amount of assets to be sent in exchange for the amount of shares specified
    * @return fee The amount of assets that will be paid as fee
    */
    function previewRedeemAndFee(uint256 shares) external view returns (uint256 assets, uint256 fee);
}

File 25 of 29 : IHATVaultsRegistry.sol
// SPDX-License-Identifier: MIT
// Disclaimer https://github.com/hats-finance/hats-contracts/blob/main/DISCLAIMER.md

pragma solidity 0.8.16;

import "./IHATVault.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/** @title Interface for the Hats.finance Vault Registry
 * @author hats.finance
 * @notice The Hats.finance Vault Registry is used to deploy Hats.finance
 * vaults and manage shared parameters.
 *
 * Hats.finance is a proactive bounty protocol for white hat hackers and
 * security experts, where projects, community members, and stakeholders
 * incentivize protocol security and responsible disclosure.
 * Hats create scalable vaults using the project’s own token. The value of the
 * bounty increases with the success of the token and project.
 *
 * The owner of the registry has the permission to set time limits and bounty
 * parameters and change vaults' info, and to set the other registry roles -
 * fee setter and arbitrator.
 * The arbitrator can challenge submitted claims for bounty payouts made by
 * vaults' committees, approve them with a different bounty percentage or
 * dismiss them.
 * The fee setter can set the fee on withdrawals on all vaults.
 *
 * This project is open-source and can be found at:
 * https://github.com/hats-finance/hats-contracts
 *
 * @dev New hats.finance vaults should be created through a call to {createVault}
 * so that they are linked to the registry
 */
interface IHATVaultsRegistry {

    // a struct with parameters for all vaults
    struct GeneralParameters {
        // vesting duration for the part of the bounty given to the hacker in HAT tokens
        uint32 hatVestingDuration;
        // vesting periods for the part of the bounty given to the hacker in HAT tokens
        uint32 hatVestingPeriods;
        // withdraw enable period. safetyPeriod starts when finished.
        uint32 withdrawPeriod;
        // withdraw disable period - time for the committee to gather and decide on actions,
        // withdrawals are not possible in this time. withdrawPeriod starts when finished.
        uint32 safetyPeriod;
        // period of time after withdrawRequestPendingPeriod where it is possible to withdraw
        // (after which withdrawals are not possible)
        uint32 withdrawRequestEnablePeriod;
        // period of time that has to pass after withdraw request until withdraw is possible
        uint32 withdrawRequestPendingPeriod;
        // period of time that has to pass after setting a pending max
        // bounty before it can be set as the new max bounty
        uint32 setMaxBountyDelay;
        // fee in ETH to be transferred with every logging of a claim
        uint256 claimFee;
    }

    /**
     * @notice Raised on {setWithdrawSafetyPeriod} if the withdraw period to
     * be set is shorter than 1 hour
     */
    error WithdrawPeriodTooShort();

    /**
     * @notice Raised on {setWithdrawSafetyPeriod} if the safety period to
     * be set is longer than 6 hours
     */
    error SafetyPeriodTooLong();

    /**
     * @notice Raised on {setWithdrawRequestParams} if the withdraw request
     * pending period to be set is shorter than 3 months
     */
    error WithdrawRequestPendingPeriodTooLong();

    /**
     * @notice Raised on {setWithdrawRequestParams} if the withdraw request
     * enabled period to be set is shorter than 6 hours
     */
    error WithdrawRequestEnabledPeriodTooShort();

    /**
     * @notice Raised on {setWithdrawRequestParams} if the withdraw request
     * enabled period to be set is longer than 100 days
     */
    error WithdrawRequestEnabledPeriodTooLong();

    /**
     * @notice Raised on {setHatVestingParams} if the vesting duration to be
     * set is longer than 180 days
     */
    error HatVestingDurationTooLong();

    /**
     * @notice Raised on {setHatVestingParams} if the vesting periods to be
     * set is 0
     */
    error HatVestingPeriodsCannotBeZero();
    
    /**
     * @notice Raised on {setHatVestingParams} if the vesting duration is 
     * smaller than the vesting periods
     */
    error HatVestingDurationSmallerThanPeriods();

    /**
     * @notice Raised on {setMaxBountyDelay} if the max bounty to be set is
     * shorter than 2 days
     */
    error DelayTooShort();

    /**
     * @notice Raised on {swapAndSend} if the amount to swap is zero
     */
    error AmountToSwapIsZero();

    /**
     * @notice Raised on {swapAndSend} if the swap was not successful
     */
    error SwapFailed();
    // Wrong amount received

    /**
     * @notice Raised on {swapAndSend} if the amount that was recieved in
     * the swap was less than the minimum amount specified
     */
    error AmountSwappedLessThanMinimum();

    /**
     * @notice Raised on {setDefaultHATBountySplit} if the split to be set is
     * greater than 20% (defined as 2000)
     */
    error TotalHatsSplitPercentageShouldBeUpToMaxHATSplit();

    /**
     * @notice Raised on {setDefaultChallengePeriod} if the challenge period
     *  to be set is shorter than 1 day
     */
    error ChallengePeriodTooShort();

    /**
     * @notice Raised on {setDefaultChallengePeriod} if the challenge period
     *  to be set is longer than 5 days
     */
    error ChallengePeriodTooLong();
        
    /**
     * @notice Raised on {setDefaultChallengeTimeOutPeriod} if the challenge
     * timeout period to be set is shorter than 1 day
     */
    error ChallengeTimeOutPeriodTooShort();

    /**
     * @notice Raised on {setDefaultChallengeTimeOutPeriod} if the challenge
     * timeout period to be set is longer than 85 days
     */
    error ChallengeTimeOutPeriodTooLong();
    
    /**
     * @notice Raised on {LogClaim} if the transaction was not sent with the
     * amount of ETH specified as {generalParameters.claimFee}
     */
    error NotEnoughFeePaid();

    /**
     * @notice Raised on {LogClaim} if the transfer of the claim fee failed
     */
    error ClaimFeeTransferFailed();

    /**
     * @notice Emitted on deployment of the registry
     * @param _hatVaultImplementation The HATVault implementation address
     * @param _HAT The HAT token address
     * @param _tokenLockFactory The token lock factory address
     * @param _generalParameters The registry's general parameters
     * @param _bountyGovernanceHAT The HAT bounty for governance
     * @param _bountyHackerHATVested The HAT bounty vested for the hacker
     * @param _hatGovernance The registry's governance
     * @param _defaultChallengePeriod The new default challenge period
     * @param _defaultChallengeTimeOutPeriod The new default challenge timeout
     * @param _defaultArbitratorCanChangeBounty Whether the arbitrator can change bounty percentage of claims
     */
    event RegistryCreated(
        address _hatVaultImplementation,
        address _HAT,
        address _tokenLockFactory,
        GeneralParameters _generalParameters,
        uint256 _bountyGovernanceHAT,
        uint256 _bountyHackerHATVested,
        address _hatGovernance,
        address _defaultArbitrator,
        uint256 _defaultChallengePeriod,
        uint256 _defaultChallengeTimeOutPeriod,
        bool _defaultArbitratorCanChangeBounty
    );

    /**
     * @notice Emitted when a claim is logged
     * @param _claimer The address of the claimer
     * @param _descriptionHash - a hash of an ipfs encrypted file which
     * describes the claim.
     */
    event LogClaim(address indexed _claimer, string _descriptionHash);

    /**
     * @notice Emitted when a new fee setter is set
     * @param _feeSetter The address of the new fee setter
     */
    event SetFeeSetter(address indexed _feeSetter);

    /**
     * @notice Emitted when new withdraw request time limits are set
     * @param _withdrawRequestPendingPeriod Time period where the withdraw
     * request is pending
     * @param _withdrawRequestEnablePeriod Time period after the peding period
     * has ended during which withdrawal is enabled
     */
    event SetWithdrawRequestParams(
        uint256 _withdrawRequestPendingPeriod,
        uint256 _withdrawRequestEnablePeriod
    );

    /**
     * @notice Emitted when a new fee for logging a claim for a bounty is set
     * @param _fee Claim fee in ETH to be transferred on any call of {logClaim}
     */
    event SetClaimFee(uint256 _fee);

    /**
     * @notice Emitted when new durations are set for withdraw period and
     * safety period
     * @param _withdrawPeriod Amount of time during which withdrawals are
     * enabled, and the bounty split can be changed by the governance
     * @param _safetyPeriod Amount of time during which claims for bounties 
     * can be submitted and withdrawals are disabled
     */
    event SetWithdrawSafetyPeriod(
        uint256 _withdrawPeriod,
        uint256 _safetyPeriod
    );

    /**
     * @notice Emitted when new HAT vesting parameters are set
     * @param _duration The duration of the vesting period
     * @param _periods The number of vesting periods
     */
    event SetHatVestingParams(uint256 _duration, uint256 _periods);

    /**
     * @notice Emitted when a new timelock delay for setting the
     * max bounty is set
     * @param _delay The time period for the delay
     */
    event SetMaxBountyDelay(uint256 _delay);

    /**
     * @notice Emitted when the UI visibility of a vault is changed
     * @param _vault The address of the vault to update
     * @param _visible Is this vault visible in the UI
     */
    event SetVaultVisibility(address indexed _vault, bool indexed _visible);

    /** @dev Emitted when a new vault is created
     * @param _vault The address of the vault to add to the registry
     * @param _params The vault initialization parameters
     */
    event VaultCreated(address indexed _vault, IHATVault.VaultInitParams _params);
    
    /** @notice Emitted when a swap of vault tokens to HAT tokens is done and
     * the HATS tokens are sent to beneficiary through vesting contract
     * @param _beneficiary Address of beneficiary
     * @param _amountSwapped Amount of vault's native tokens that was swapped
     * @param _amountSent Amount of HAT tokens sent to beneficiary
     * @param _tokenLock Address of the token lock contract that holds the HAT
     * tokens (address(0) if no token lock is used)
     */
    event SwapAndSend(
        address indexed _beneficiary,
        uint256 _amountSwapped,
        uint256 _amountSent,
        address indexed _tokenLock
    );

    /**
     * @notice Emitted when a new default HAT bounty split is set
     * @param _defaultBountyGovernanceHAT The new default HAT bounty part sent to governance
     * @param _defaultBountyHackerHATVested The new default HAT bounty part vseted for the hacker
     */
    event SetDefaultHATBountySplit(uint256 _defaultBountyGovernanceHAT, uint256 _defaultBountyHackerHATVested);

    /**
     * @notice Emitted when a new default arbitrator is set
     * @param _defaultArbitrator The address of the new arbitrator
     */
    event SetDefaultArbitrator(address indexed _defaultArbitrator);

    /**
     * @notice Emitted when a new default challenge period is set
     * @param _defaultChallengePeriod The new default challenge period
     */ 
    event SetDefaultChallengePeriod(uint256 _defaultChallengePeriod);

    /**
     * @notice Emitted when a new default challenge timeout period is set
     * @param _defaultChallengeTimeOutPeriod The new default challenge timeout
     * period
     */
    event SetDefaultChallengeTimeOutPeriod(uint256 _defaultChallengeTimeOutPeriod);

    /**
     * @notice Emitted when the default arbitrator can change bounty is set
     * @param _defaultArbitratorCanChangeBounty Whether the arbitrator can change bounty of claims
     */
    event SetDefaultArbitratorCanChangeBounty(bool _defaultArbitratorCanChangeBounty);

    /** @notice Emitted when the system is put into emergency pause/unpause
     * @param _isEmergencyPaused Is the system in an emergency pause
     */
    event SetEmergencyPaused(bool _isEmergencyPaused);

    /**
     * @notice Emitted when a new swap token is set
     * @param _swapToken The new swap token address
     */
    event SetSwapToken(address indexed _swapToken);

    /**
     * @notice Called by governance to pause/unpause the system in case of an
     * emergency
     * @param _isEmergencyPaused Is the system in an emergency pause
     */
    function setEmergencyPaused(bool _isEmergencyPaused) external;

    /**
     * @notice Called by governance to set a new swap token
     * @param _swapToken the new swap token address
     */
    function setSwapToken(address _swapToken) external;

    /**
     * @notice Emit an event that includes the given _descriptionHash
     * This can be used by the claimer as evidence that she had access to the
     * information at the time of the call
     * if a {generalParameters.claimFee} > 0, the caller must send that amount
     * of ETH for the claim to succeed
     * @param _descriptionHash - a hash of an IPFS encrypted file which 
     * describes the claim.
     */
    function logClaim(string calldata _descriptionHash) external payable;

    /**
     * @notice Called by governance to set the default percentage of each claim bounty
     * that will be swapped for hats and sent to the governance or vested for the hacker
     * @param _defaultBountyGovernanceHAT The HAT bounty for governance
     * @param _defaultBountyHackerHATVested The HAT bounty vested for the hacker
     */
    function setDefaultHATBountySplit(
        uint16 _defaultBountyGovernanceHAT,
        uint16 _defaultBountyHackerHATVested
    ) 
        external;

    /** 
     * @dev Check that a given hats bounty split is legal, meaning that:
     *   Each entry is a number between 0 and less than `MAX_HAT_SPLIT`.
     *   Total splits should be less than `MAX_HAT_SPLIT`.
     * function will revert in case the bounty split is not legal.
     * @param _bountyGovernanceHAT The HAT bounty for governance
     * @param _bountyHackerHATVested The HAT bounty vested for the hacker
     */
    function validateHATSplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested)
         external
         pure;

    /**
     * @notice Called by governance to set the default arbitrator.
     * @param _defaultArbitrator The default arbitrator address
     */
    function setDefaultArbitrator(address _defaultArbitrator) external;

    /**
     * @notice Called by governance to set the default challenge period
     * @param _defaultChallengePeriod The default challenge period
     */
    function setDefaultChallengePeriod(uint32 _defaultChallengePeriod) 
        external;

    /**
     * @notice Called by governance to set the default challenge timeout
     * @param _defaultChallengeTimeOutPeriod The Default challenge timeout
     */
    function setDefaultChallengeTimeOutPeriod(
        uint32 _defaultChallengeTimeOutPeriod
    ) 
        external;

    /**
     * @notice Called by governance to set Whether the arbitrator can change bounty of claims.
     * @param _defaultArbitratorCanChangeBounty The default for whether the arbitrator can change bounty of claims
     */
    function setDefaultArbitratorCanChangeBounty(bool _defaultArbitratorCanChangeBounty) external;

    /**
     * @notice Check that the given challenge period is legal, meaning that it
     * is greater than 1 day and less than 5 days.
     * @param _challengePeriod The challenge period to check
     */
    function validateChallengePeriod(uint32 _challengePeriod) external pure;

    /**
     * @notice Check that the given challenge timeout period is legal, meaning
     * that it is greater than 2 days and less than 85 days.
     * @param _challengeTimeOutPeriod The challenge timeout period to check
     */
    function validateChallengeTimeOutPeriod(uint32 _challengeTimeOutPeriod) external pure;
   
    /**
     * @notice Called by governance to set the fee setter role
     * @param _feeSetter Address of new fee setter
     */
    function setFeeSetter(address _feeSetter) external;

    /**
     * @notice Called by governance to set time limits for withdraw requests
     * @param _withdrawRequestPendingPeriod Time period where the withdraw
     * request is pending
     * @param _withdrawRequestEnablePeriod Time period after the peding period
     * has ended during which withdrawal is enabled
     */
    function setWithdrawRequestParams(
        uint32 _withdrawRequestPendingPeriod,
        uint32  _withdrawRequestEnablePeriod
    )
        external;

    /**
     * @notice Called by governance to set the fee for logging a claim for a
     * bounty in any vault.
     * @param _fee Claim fee in ETH to be transferred on any call of
     * {logClaim}
     */
    function setClaimFee(uint256 _fee) external;

    /**
     * @notice Called by governance to set the withdraw period and safety
     * period, which are always interchanging.
     * The safety period is time that the committee can submit claims for 
     * bounty payouts, and during which withdrawals are disabled and the
     * bounty split cannot be changed.
     * @param _withdrawPeriod Amount of time during which withdrawals are
     * enabled, and the bounty split can be changed by the governance. Must be
     * at least 1 hour.
     * @param _safetyPeriod Amount of time during which claims for bounties 
     * can be submitted and withdrawals are disabled. Must be at most 6 hours.
     */
    function setWithdrawSafetyPeriod(
        uint32 _withdrawPeriod,
        uint32 _safetyPeriod
    ) 
        external;

    /**
     * @notice Called by governance to set vesting params for rewarding hackers
     * with rewardToken, for all vaults
     * @param _duration Duration of the vesting period. Must be less than 180
     * days.
     * @param _periods The number of vesting periods. Must be more than 0 and 
     * less then the vesting duration.
     */
    function setHatVestingParams(uint32 _duration, uint32 _periods) external;

    /**
     * @notice Called by governance to set the timelock delay for setting the
     * max bounty (the time between setPendingMaxBounty and setMaxBounty)
     * @param _delay The time period for the delay. Must be at least 2 days.
     */
    function setMaxBountyDelay(uint32 _delay) external;

    /**
     * @notice Create a new vault
     * NOTE: Vaults should not use tokens which do not guarantee that the 
     * amount specified is the amount transferred
     * @param _params The vault initialization parameters
     * @return vault The address of the new vault
     */
    function createVault(IHATVault.VaultInitParams calldata _params) external returns(address vault);

    /**
     * @notice Called by governance to change the UI visibility of a vault
     * @param _vault The address of the vault to update
     * @param _visible Is this vault visible in the UI
     * This parameter can be used by the UI to include or exclude the vault
     */
    function setVaultVisibility(address _vault, bool _visible) external;

    /**
     * @notice Transfer the part of the bounty that is supposed to be swapped
     * into HAT tokens from the HATVault to the registry, and keep track of
     * the amounts to be swapped and sent/burnt in a later transaction
     * @param _asset The vault's native token
     * @param _hacker The address of the beneficiary of the bounty
     * @param _hackersHatReward The amount of the vault's native token to be
     * swapped to HAT tokens and sent to the hacker via a vesting contract
     * @param _governanceHatReward The amount of the vault's native token to
     * be swapped to HAT tokens and sent to governance
     */
    function addTokensToSwap(
        IERC20 _asset,
        address _hacker,
        uint256 _hackersHatReward,
        uint256 _governanceHatReward
    ) external;

    /**
     * @notice Called by governance to swap the given asset to HAT tokens and 
     * distribute the HAT tokens: Send to governance their share and send to
     * beneficiaries their share through a vesting contract.
     * @param _asset The address of the token to be swapped to HAT tokens
     * @param _beneficiaries Addresses of beneficiaries
     * @param _amountOutMinimum Minimum amount of HAT tokens at swap
     * @param _routingContract Routing contract to call for the swap
     * @param _routingPayload Payload to send to the _routingContract for the
     * swap
     */
    function swapAndSend(
        address _asset,
        address[] calldata _beneficiaries,
        uint256 _amountOutMinimum,
        address _routingContract,
        bytes calldata _routingPayload
    ) external;
  
    /**
     * @notice Returns the withdraw enable period for all vaults. The safety
     * period starts when finished.
     * @return Withdraw enable period for all vaults
     */
    function getWithdrawPeriod() external view returns (uint256);

    /**
     * @notice Returns the withdraw disable period - time for the committee to
     * gather and decide on actions, withdrawals are not possible in this
     * time. The withdraw period starts when finished.
     * @return Safety period for all vaults
     */
    function getSafetyPeriod() external view returns (uint256);

    /**
     * @notice Returns the withdraw request enable period for all vaults -
     * period of time after withdrawRequestPendingPeriod where it is possible
     * to withdraw, and after which withdrawals are not possible.
     * @return Withdraw request enable period for all vaults
     */
    function getWithdrawRequestEnablePeriod() external view returns (uint256);

    /**
     * @notice Returns the withdraw request pending period for all vaults -
     * period of time that has to pass after withdraw request until withdraw
     * is possible
     * @return Withdraw request pending period for all vaults
     */
    function getWithdrawRequestPendingPeriod() external view returns (uint256);

    /**
     * @notice Returns the set max bounty delay for all vaults - period of
     * time that has to pass after setting a pending max bounty before it can
     * be set as the new max bounty
     * @return Set max bounty delay for all vaults
     */
    function getSetMaxBountyDelay() external view returns (uint256);

    /**
     * @notice Returns the number of vaults that have been previously created
     * @return The number of vaults in the registry
     */
    function getNumberOfVaults() external view returns(uint256);

}

File 26 of 29 : IRewardController.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

interface IRewardController {
    
    error EpochLengthZero();
    // Not enough rewards to transfer to user
    error NotEnoughRewardsToTransferToUser();

    event RewardControllerCreated(
        address _rewardToken,
        address _governance,
        uint256 _startBlock,
        uint256 _epochLength,
        uint256[24] _epochRewardPerBlock
    );
    event SetEpochRewardPerBlock(uint256[24] _epochRewardPerBlock);
    event SetAllocPoint(address indexed _vault, uint256 _prevAllocPoint, uint256 _allocPoint);
    event VaultUpdated(address indexed _vault, uint256 _rewardPerShare, uint256 _lastProcessedVaultUpdate);
    event UserBalanceCommitted(address indexed _vault, address indexed _user, uint256 _unclaimedReward, uint256 _rewardDebt);
    event ClaimReward(address indexed _vault, address indexed _user, uint256 _amount);

    /**
     * @notice Initializes the reward controller
     * @param _rewardToken The address of the ERC20 token to be distributed as rewards
     * @param _governance The hats governance address, to be given ownership of the reward controller
     * @param _startRewardingBlock The block number from which to start rewarding
     * @param _epochLength The length of a rewarding epoch
     * @param _epochRewardPerBlock The reward per block for each of the 24 epochs
     */
    function initialize(
        address _rewardToken,
        address _governance,
        uint256 _startRewardingBlock,
        uint256 _epochLength,
        uint256[24] calldata _epochRewardPerBlock
    ) external;

    /**
     * @notice Called by the owner to set the allocation points for a vault, meaning the
     * vault's relative share of the total rewards
     * @param _vault The address of the vault
     * @param _allocPoint The allocation points for the vault
     */
    function setAllocPoint(address _vault, uint256 _allocPoint) external;

    /**
    * @notice Update the vault's reward per share, not more then once per block
    * @param _vault The vault's address
    */
    function updateVault(address _vault) external;

    /**
    * @notice Called by the owner to set reward per epoch
    * Reward can only be set for epochs which have not yet started
    * @param _epochRewardPerBlock reward per block for each epoch
    */
    function setEpochRewardPerBlock(uint256[24] calldata _epochRewardPerBlock) external;

    /**
    * @notice Called by the vault to update a user claimable reward after deposit or withdraw.
    * This call should never revert.
    * @param _user The user address to updare rewards for
    * @param _sharesChange The user of shared the user deposited or withdrew
    * @param _isDeposit Whether user deposited or withdrew
    */
    function commitUserBalance(address _user, uint256 _sharesChange, bool _isDeposit) external;
    /**
    * @notice Transfer to the specified user their pending share of rewards.
    * @param _vault The vault address
    * @param _user The user address to claim for
    */
    function claimReward(address _vault, address _user) external;

    /**
    * @notice Calculate rewards for a vault by iterating over the history of totalAllocPoints updates,
    * and sum up all rewards periods from vault.lastRewardBlock until current block number.
    * @param _vault The vault address
    * @param _fromBlock The block from which to start calculation
    * @return reward The amount of rewards for the vault
    */
    function getVaultReward(address _vault, uint256 _fromBlock) external view returns(uint256 reward);

    /**
    * @notice Calculate the amount of rewards a user can claim for having contributed to a specific vault
    * @param _vault The vault address
    * @param _user The user for which the reward is calculated
    */
    function getPendingReward(address _vault, address _user) external view returns (uint256);

    /**
    * @notice Called by the owner to transfer any tokens held in this contract to the owner
    * @param _token The token to sweep
    * @param _amount The amount of token to sweep
    */
    function sweepToken(IERC20Upgradeable _token, uint256 _amount) external;

}

File 27 of 29 : ITokenLock.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.16;
pragma experimental ABIEncoderV2;

interface ITokenLock {
    enum Revocability { NotSet, Enabled, Disabled }

    // -- Value Transfer --

    function release() external;

    function withdrawSurplus(uint256 _amount) external;

    function revoke() external;

    // -- Balances --

    function currentBalance() external view returns (uint256);

    // -- Time & Periods --

    function currentTime() external view returns (uint256);

    function duration() external view returns (uint256);

    function sinceStartTime() external view returns (uint256);

    function amountPerPeriod() external view returns (uint256);

    function periodDuration() external view returns (uint256);

    function currentPeriod() external view returns (uint256);

    function passedPeriods() external view returns (uint256);

    // -- Locking & Release Schedule --

    function availableAmount() external view returns (uint256);

    function vestedAmount() external view returns (uint256);

    function releasableAmount() external view returns (uint256);

    function totalOutstandingAmount() external view returns (uint256);

    function surplusAmount() external view returns (uint256);
}

File 28 of 29 : ITokenLockFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "./ITokenLock.sol";

interface ITokenLockFactory {
    // -- Factory --
    function setMasterCopy(address _masterCopy) external;

    function createTokenLock(
        address _token,
        address _owner,
        address _beneficiary,
        uint256 _managedAmount,
        uint256 _startTime,
        uint256 _endTime,
        uint256 _periods,
        uint256 _releaseStartTime,
        uint256 _vestingCliffTime,
        ITokenLock.Revocability _revocable,
        bool _canDelegate
    ) external returns(address contractAddress);
}

File 29 of 29 : TokenLockFactory.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.16;

import "@openzeppelin/contracts/proxy/Clones.sol";
import "./ITokenLockFactory.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";


/**
 * @title TokenLockFactory
 *  a factory of TokenLock contracts.
 *
 * This contract receives funds to make the process of creating TokenLock contracts
 * easier by distributing them the initial tokens to be managed.
 */
contract TokenLockFactory is ITokenLockFactory, Ownable {
    // -- State --

    address public masterCopy;

    // -- Events --

    event MasterCopyUpdated(address indexed masterCopy);

    event TokenLockCreated(
        address indexed contractAddress,
        bytes32 indexed initHash,
        address indexed beneficiary,
        address token,
        uint256 managedAmount,
        uint256 startTime,
        uint256 endTime,
        uint256 periods,
        uint256 releaseStartTime,
        uint256 vestingCliffTime,
        ITokenLock.Revocability revocable,
        bool canDelegate
    );

    /**
     * Constructor.
     * @param _masterCopy Address of the master copy to use to clone proxies
     * @param _governance Owner of the factory
     */
    constructor(address _masterCopy, address _governance) {
        setMasterCopy(_masterCopy);
        _transferOwnership(_governance);
    }

    // -- Factory --
    /**
     * @notice Creates and fund a new token lock wallet using a minimum proxy
     * @param _token token to time lock
     * @param _owner Address of the contract owner
     * @param _beneficiary Address of the beneficiary of locked tokens
     * @param _managedAmount Amount of tokens to be managed by the lock contract
     * @param _startTime Start time of the release schedule
     * @param _endTime End time of the release schedule
     * @param _periods Number of periods between start time and end time
     * @param _releaseStartTime Override time for when the releases start
     * @param _revocable Whether the contract is revocable
     * @param _canDelegate Whether the contract should call delegate
     */
    function createTokenLock(
        address _token,
        address _owner,
        address _beneficiary,
        uint256 _managedAmount,
        uint256 _startTime,
        uint256 _endTime,
        uint256 _periods,
        uint256 _releaseStartTime,
        uint256 _vestingCliffTime,
        ITokenLock.Revocability _revocable,
        bool _canDelegate
    ) external override returns(address contractAddress) {
        // Create contract using a minimal proxy and call initializer
        bytes memory initializer = abi.encodeWithSignature(
            "initialize(address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,uint8,bool)",
            _owner,
            _beneficiary,
            _token,
            _managedAmount,
            _startTime,
            _endTime,
            _periods,
            _releaseStartTime,
            _vestingCliffTime,
            _revocable,
            _canDelegate
        );

        contractAddress = deployProxyPrivate(initializer,
        _beneficiary,
        _token,
        _managedAmount,
        _startTime,
        _endTime,
        _periods,
        _releaseStartTime,
        _vestingCliffTime,
        _revocable,
        _canDelegate);
    }

    /**
     * @notice Sets the masterCopy bytecode to use to create clones of TokenLock contracts
     * @param _masterCopy Address of contract bytecode to factory clone
     */
    function setMasterCopy(address _masterCopy) public override onlyOwner {
        require(_masterCopy != address(0), "MasterCopy cannot be zero");
        masterCopy = _masterCopy;
        emit MasterCopyUpdated(_masterCopy);
    }

    //this private function is to handle stack too deep issue
    function  deployProxyPrivate(
        bytes memory _initializer,
        address _beneficiary,
        address _token,
        uint256 _managedAmount,
        uint256 _startTime,
        uint256 _endTime,
        uint256 _periods,
        uint256 _releaseStartTime,
        uint256 _vestingCliffTime,
        ITokenLock.Revocability _revocable,
        bool _canDelegate
    ) private returns (address contractAddress) {

        contractAddress = Clones.clone(masterCopy);

        Address.functionCall(contractAddress, _initializer);

        emit TokenLockCreated(
            contractAddress,
            keccak256(_initializer),
            _beneficiary,
            _token,
            _managedAmount,
            _startTime,
            _endTime,
            _periods,
            _releaseStartTime,
            _vestingCliffTime,
            _revocable,
            _canDelegate
        );
    }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_hatVaultImplementation","type":"address"},{"internalType":"address","name":"_hatGovernance","type":"address"},{"internalType":"address","name":"_defaultArbitrator","type":"address"},{"internalType":"address","name":"_HAT","type":"address"},{"internalType":"uint16","name":"_bountyGovernanceHAT","type":"uint16"},{"internalType":"uint16","name":"_bountyHackerHATVested","type":"uint16"},{"internalType":"contract ITokenLockFactory","name":"_tokenLockFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AmountSwappedLessThanMinimum","type":"error"},{"inputs":[],"name":"AmountToSwapIsZero","type":"error"},{"inputs":[],"name":"ChallengePeriodTooLong","type":"error"},{"inputs":[],"name":"ChallengePeriodTooShort","type":"error"},{"inputs":[],"name":"ChallengeTimeOutPeriodTooLong","type":"error"},{"inputs":[],"name":"ChallengeTimeOutPeriodTooShort","type":"error"},{"inputs":[],"name":"ClaimFeeTransferFailed","type":"error"},{"inputs":[],"name":"DelayTooShort","type":"error"},{"inputs":[],"name":"HatVestingDurationSmallerThanPeriods","type":"error"},{"inputs":[],"name":"HatVestingDurationTooLong","type":"error"},{"inputs":[],"name":"HatVestingPeriodsCannotBeZero","type":"error"},{"inputs":[],"name":"NotEnoughFeePaid","type":"error"},{"inputs":[],"name":"SafetyPeriodTooLong","type":"error"},{"inputs":[],"name":"SwapFailed","type":"error"},{"inputs":[],"name":"TotalHatsSplitPercentageShouldBeUpToMaxHATSplit","type":"error"},{"inputs":[],"name":"WithdrawPeriodTooShort","type":"error"},{"inputs":[],"name":"WithdrawRequestEnabledPeriodTooLong","type":"error"},{"inputs":[],"name":"WithdrawRequestEnabledPeriodTooShort","type":"error"},{"inputs":[],"name":"WithdrawRequestPendingPeriodTooLong","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_claimer","type":"address"},{"indexed":false,"internalType":"string","name":"_descriptionHash","type":"string"}],"name":"LogClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_hatVaultImplementation","type":"address"},{"indexed":false,"internalType":"address","name":"_HAT","type":"address"},{"indexed":false,"internalType":"address","name":"_tokenLockFactory","type":"address"},{"components":[{"internalType":"uint32","name":"hatVestingDuration","type":"uint32"},{"internalType":"uint32","name":"hatVestingPeriods","type":"uint32"},{"internalType":"uint32","name":"withdrawPeriod","type":"uint32"},{"internalType":"uint32","name":"safetyPeriod","type":"uint32"},{"internalType":"uint32","name":"withdrawRequestEnablePeriod","type":"uint32"},{"internalType":"uint32","name":"withdrawRequestPendingPeriod","type":"uint32"},{"internalType":"uint32","name":"setMaxBountyDelay","type":"uint32"},{"internalType":"uint256","name":"claimFee","type":"uint256"}],"indexed":false,"internalType":"struct IHATVaultsRegistry.GeneralParameters","name":"_generalParameters","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"_bountyGovernanceHAT","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_bountyHackerHATVested","type":"uint256"},{"indexed":false,"internalType":"address","name":"_hatGovernance","type":"address"},{"indexed":false,"internalType":"address","name":"_defaultArbitrator","type":"address"},{"indexed":false,"internalType":"uint256","name":"_defaultChallengePeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_defaultChallengeTimeOutPeriod","type":"uint256"},{"indexed":false,"internalType":"bool","name":"_defaultArbitratorCanChangeBounty","type":"bool"}],"name":"RegistryCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"SetClaimFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_defaultArbitrator","type":"address"}],"name":"SetDefaultArbitrator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_defaultArbitratorCanChangeBounty","type":"bool"}],"name":"SetDefaultArbitratorCanChangeBounty","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_defaultChallengePeriod","type":"uint256"}],"name":"SetDefaultChallengePeriod","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_defaultChallengeTimeOutPeriod","type":"uint256"}],"name":"SetDefaultChallengeTimeOutPeriod","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_defaultBountyGovernanceHAT","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_defaultBountyHackerHATVested","type":"uint256"}],"name":"SetDefaultHATBountySplit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_isEmergencyPaused","type":"bool"}],"name":"SetEmergencyPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_feeSetter","type":"address"}],"name":"SetFeeSetter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_duration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_periods","type":"uint256"}],"name":"SetHatVestingParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_delay","type":"uint256"}],"name":"SetMaxBountyDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_swapToken","type":"address"}],"name":"SetSwapToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_vault","type":"address"},{"indexed":true,"internalType":"bool","name":"_visible","type":"bool"}],"name":"SetVaultVisibility","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_withdrawRequestPendingPeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_withdrawRequestEnablePeriod","type":"uint256"}],"name":"SetWithdrawRequestParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_withdrawPeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_safetyPeriod","type":"uint256"}],"name":"SetWithdrawSafetyPeriod","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountSwapped","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountSent","type":"uint256"},{"indexed":true,"internalType":"address","name":"_tokenLock","type":"address"}],"name":"SwapAndSend","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_vault","type":"address"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"contract IRewardController[]","name":"rewardControllers","type":"address[]"},{"internalType":"uint32","name":"vestingDuration","type":"uint32"},{"internalType":"uint32","name":"vestingPeriods","type":"uint32"},{"internalType":"uint16","name":"maxBounty","type":"uint16"},{"components":[{"internalType":"uint16","name":"hackerVested","type":"uint16"},{"internalType":"uint16","name":"hacker","type":"uint16"},{"internalType":"uint16","name":"committee","type":"uint16"}],"internalType":"struct IHATVault.BountySplit","name":"bountySplit","type":"tuple"},{"internalType":"contract IERC20","name":"asset","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"committee","type":"address"},{"internalType":"bool","name":"isPaused","type":"bool"},{"internalType":"string","name":"descriptionHash","type":"string"}],"indexed":false,"internalType":"struct IHATVault.VaultInitParams","name":"_params","type":"tuple"}],"name":"VaultCreated","type":"event"},{"inputs":[],"name":"HAT","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HUNDRED_PERCENT","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_HAT_SPLIT","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_asset","type":"address"},{"internalType":"address","name":"_hacker","type":"address"},{"internalType":"uint256","name":"_hackersHatReward","type":"uint256"},{"internalType":"uint256","name":"_governanceHatReward","type":"uint256"}],"name":"addTokensToSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"contract IRewardController[]","name":"rewardControllers","type":"address[]"},{"internalType":"uint32","name":"vestingDuration","type":"uint32"},{"internalType":"uint32","name":"vestingPeriods","type":"uint32"},{"internalType":"uint16","name":"maxBounty","type":"uint16"},{"components":[{"internalType":"uint16","name":"hackerVested","type":"uint16"},{"internalType":"uint16","name":"hacker","type":"uint16"},{"internalType":"uint16","name":"committee","type":"uint16"}],"internalType":"struct IHATVault.BountySplit","name":"bountySplit","type":"tuple"},{"internalType":"contract IERC20","name":"asset","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"committee","type":"address"},{"internalType":"bool","name":"isPaused","type":"bool"},{"internalType":"string","name":"descriptionHash","type":"string"}],"internalType":"struct IHATVault.VaultInitParams","name":"_params","type":"tuple"}],"name":"createVault","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultArbitrator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultArbitratorCanChangeBounty","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultBountyGovernanceHAT","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultBountyHackerHATVested","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultChallengePeriod","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultChallengeTimeOutPeriod","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"generalParameters","outputs":[{"internalType":"uint32","name":"hatVestingDuration","type":"uint32"},{"internalType":"uint32","name":"hatVestingPeriods","type":"uint32"},{"internalType":"uint32","name":"withdrawPeriod","type":"uint32"},{"internalType":"uint32","name":"safetyPeriod","type":"uint32"},{"internalType":"uint32","name":"withdrawRequestEnablePeriod","type":"uint32"},{"internalType":"uint32","name":"withdrawRequestPendingPeriod","type":"uint32"},{"internalType":"uint32","name":"setMaxBountyDelay","type":"uint32"},{"internalType":"uint256","name":"claimFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumberOfVaults","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSafetyPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSetMaxBountyDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawRequestEnablePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawRequestPendingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"governanceHatReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"hackersHatReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hatVaultImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"hatVaults","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isEmergencyPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isVaultVisible","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_descriptionHash","type":"string"}],"name":"logClaim","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setClaimFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_defaultArbitrator","type":"address"}],"name":"setDefaultArbitrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_defaultArbitratorCanChangeBounty","type":"bool"}],"name":"setDefaultArbitratorCanChangeBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_defaultChallengePeriod","type":"uint32"}],"name":"setDefaultChallengePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_defaultChallengeTimeOutPeriod","type":"uint32"}],"name":"setDefaultChallengeTimeOutPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_defaultBountyGovernanceHAT","type":"uint16"},{"internalType":"uint16","name":"_defaultBountyHackerHATVested","type":"uint16"}],"name":"setDefaultHATBountySplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isEmergencyPaused","type":"bool"}],"name":"setEmergencyPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeSetter","type":"address"}],"name":"setFeeSetter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_duration","type":"uint32"},{"internalType":"uint32","name":"_periods","type":"uint32"}],"name":"setHatVestingParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_delay","type":"uint32"}],"name":"setMaxBountyDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapToken","type":"address"}],"name":"setSwapToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"bool","name":"_visible","type":"bool"}],"name":"setVaultVisibility","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_withdrawRequestPendingPeriod","type":"uint32"},{"internalType":"uint32","name":"_withdrawRequestEnablePeriod","type":"uint32"}],"name":"setWithdrawRequestParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_withdrawPeriod","type":"uint32"},{"internalType":"uint32","name":"_safetyPeriod","type":"uint32"}],"name":"setWithdrawSafetyPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address[]","name":"_beneficiaries","type":"address[]"},{"internalType":"uint256","name":"_amountOutMinimum","type":"uint256"},{"internalType":"address","name":"_routingContract","type":"address"},{"internalType":"bytes","name":"_routingPayload","type":"bytes"}],"name":"swapAndSend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenLockFactory","outputs":[{"internalType":"contract ITokenLockFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_challengePeriod","type":"uint32"}],"name":"validateChallengePeriod","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint32","name":"_challengeTimeOutPeriod","type":"uint32"}],"name":"validateChallengeTimeOutPeriod","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint16","name":"_bountyGovernanceHAT","type":"uint16"},{"internalType":"uint16","name":"_bountyHackerHATVested","type":"uint16"}],"name":"validateHATSplit","outputs":[],"stateMutability":"pure","type":"function"}]

60c06040523480156200001157600080fd5b506040516200328738038062003287833981016040819052620000349162000435565b6200003f336200037d565b6200004a866200037d565b6001600160a01b03878116608052600780546001600160a01b0319169186169190911790556200007b8383620003cd565b806001600160a01b031660a0816001600160a01b0316815250506040518061010001604052806276a70063ffffffff168152602001605a63ffffffff168152602001619ab063ffffffff168152602001610e1063ffffffff16815260200162093a8063ffffffff16815260200162093a8063ffffffff1681526020016202a30063ffffffff1681526020016000815250600560008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160000160106101000a81548163ffffffff021916908363ffffffff16021790555060a08201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060c08201518160000160186101000a81548163ffffffff021916908363ffffffff16021790555060e0820151816001015590505082600860146101000a81548161ffff021916908361ffff16021790555081600860166101000a81548161ffff021916908361ffff16021790555084600960006101000a8154816001600160a01b0302191690836001600160a01b031602179055506203f480600960166101000a81548163ffffffff021916908363ffffffff160217905550622e24806009601a6101000a81548163ffffffff021916908363ffffffff1602179055506001600960146101000a81548160ff0219169083151502179055507f77d457972e962930e8a217de64210a70cd59348e32d95e7f6c7513deb5d71f92878583600587878c8c600960169054906101000a900463ffffffff166009601a9054906101000a900463ffffffff16600960149054906101000a900460ff16604051620003689b9a99989796959493929190620004d9565b60405180910390a1505050505050506200062a565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6107d0620003dc8284620005f9565b61ffff16111562000400576040516307af556960e21b815260040160405180910390fd5b5050565b6001600160a01b03811681146200041a57600080fd5b50565b805161ffff811681146200043057600080fd5b919050565b600080600080600080600060e0888a0312156200045157600080fd5b87516200045e8162000404565b6020890151909750620004718162000404565b6040890151909650620004848162000404565b6060890151909550620004978162000404565b9350620004a7608089016200041d565b9250620004b760a089016200041d565b915060c0880151620004c98162000404565b8091505092959891949750929550565b60006102408201905060018060a01b03808e168352808d166020840152808c16604084015250895463ffffffff8082166060850152808260201c1660808501526200053160a08501828460401c1663ffffffff169052565b6200054960c08501828460601c1663ffffffff169052565b6200056160e08501828460801c1663ffffffff169052565b6200057a6101008501828460a01c1663ffffffff169052565b620005936101208501828460c01c1663ffffffff169052565b50506001999099015461014082015261ffff978816610160820152959096166101808601526001600160a01b039384166101a0860152919092166101c084015263ffffffff9182166101e084015216610200820152901515610220909101529392505050565b61ffff8181168382160190808211156200062357634e487b7160e01b600052601160045260246000fd5b5092915050565b60805160a051612c296200065e6000396000818161035101526116f501526000818161041c0152610d270152612c296000f3fe6080604052600436106102935760003560e01c806377c9cd8c1161015a578063d3e0bc56116100c1578063ea55f18e1161007a578063ea55f18e14610907578063eeb338871461091d578063f2fde38b1461093f578063f4bde0971461095f578063f51a90e414610981578063fb9a8740146109a557600080fd5b8063d3e0bc56146107bf578063db0f267b146107d4578063db399053146107f4578063df1b1ebe14610824578063e2b001bf146108d4578063e367497d146108f457600080fd5b8063a3fe95b211610113578063a3fe95b2146106f2578063a8acd94614610712578063aec1d10814610732578063b19805af14610752578063b851b7ca14610772578063bcfdfc581461079257600080fd5b806377c9cd8c1461063257806387cf3ef414610654578063891ce467146106745780638da5cb5b1461069457806392b2fd34146106b25780639d0ab89f146106d257600080fd5b80632e75ab50116101fe5780634f9627b0116101b75780634f9627b01461058557806354492078146105a55780635dd11415146105c75780636c0d189a146105e75780636ed93dd014610607578063715018a61461061d57600080fd5b80632e75ab501461049f5780632ea7f250146104bf5780633449a865146104eb578063354d75b91461050b5780633bd495ce14610543578063403661fb1461056557600080fd5b80631a95cf06116102505780631a95cf06146103ca5780631b2cae6c146103ea57806321c0437a1461040a5780632350e92d1461043e578063250c2cd91461045e578063290d10c41461047e57600080fd5b806306c6e3b81461029857806311566d74146102d5578063116dbc931461030e57806311c2aae31461033f578063120c1a7e14610373578063165db9a9146103a8575b600080fd5b3480156102a457600080fd5b506102b86102b3366004612399565b6109c5565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102e157600080fd5b506009546102f990600160b01b900463ffffffff1681565b60405163ffffffff90911681526020016102cc565b34801561031a57600080fd5b5060095461032f90600160a01b900460ff1681565b60405190151581526020016102cc565b34801561034b57600080fd5b506102b87f000000000000000000000000000000000000000000000000000000000000000081565b34801561037f57600080fd5b5060085461039590600160b01b900461ffff1681565b60405161ffff90911681526020016102cc565b3480156103b457600080fd5b506103c86103c33660046123c6565b6109ef565b005b3480156103d657600080fd5b506103c86103e53660046123e1565b610a46565b3480156103f657600080fd5b506103c861040536600461242d565b610b23565b34801561041657600080fd5b506102b87f000000000000000000000000000000000000000000000000000000000000000081565b34801561044a57600080fd5b506103c86104593660046123e1565b610b83565b34801561046a57600080fd5b506103c86104793660046123c6565b610c7e565b34801561048a57600080fd5b5060095461032f90600160a81b900460ff1681565b3480156104ab57600080fd5b506103c86104ba366004612399565b610ce3565b3480156104cb57600080fd5b50600554600160401b900463ffffffff165b6040519081526020016102cc565b3480156104f757600080fd5b506007546102b8906001600160a01b031681565b34801561051757600080fd5b506104dd61052636600461246a565b600360209081526000928352604080842090915290825290205481565b34801561054f57600080fd5b50600554600160a01b900463ffffffff166104dd565b34801561057157600080fd5b506102b86105803660046124a3565b610d20565b34801561059157600080fd5b506103c86105a03660046123e1565b610e38565b3480156105b157600080fd5b5060085461039590600160a01b900461ffff1681565b3480156105d357600080fd5b506103c86105e23660046124df565b610f1f565b3480156105f357600080fd5b506103c86106023660046123c6565b610fb0565b34801561061357600080fd5b5061039561271081565b34801561062957600080fd5b506103c8611036565b34801561063e57600080fd5b50600554600160c01b900463ffffffff166104dd565b34801561066057600080fd5b506008546102b8906001600160a01b031681565b34801561068057600080fd5b506103c861068f3660046123c6565b61104a565b3480156106a057600080fd5b506000546001600160a01b03166102b8565b3480156106be57600080fd5b506103c86106cd366004612537565b61109e565b3480156106de57600080fd5b506103c86106ed36600461242d565b61111e565b3480156106fe57600080fd5b506103c861070d3660046123c6565b611173565b34801561071e57600080fd5b506103c861072d366004612561565b6111d8565b34801561073e57600080fd5b506103c861074d36600461257e565b61122a565b34801561075e57600080fd5b506103c861076d366004612561565b611286565b34801561077e57600080fd5b506103c861078d366004612561565b6112d8565b34801561079e57600080fd5b506104dd6107ad366004612561565b60046020526000908152604090205481565b3480156107cb57600080fd5b506001546104dd565b3480156107e057600080fd5b506009546102b8906001600160a01b031681565b34801561080057600080fd5b5061032f61080f366004612561565b60026020526000908152604090205460ff1681565b34801561083057600080fd5b506005546006546108849163ffffffff808216926401000000008304821692600160401b8104831692600160601b8204811692600160801b8304821692600160a01b8104831692600160c01b909104169088565b6040805163ffffffff998a16815297891660208901529588169587019590955292861660608601529085166080850152841660a084015290921660c082015260e0810191909152610100016102cc565b3480156108e057600080fd5b506103c86108ef3660046125f5565b61132a565b6103c86109023660046126c1565b61191e565b34801561091357600080fd5b506103956107d081565b34801561092957600080fd5b50600554600160601b900463ffffffff166104dd565b34801561094b57600080fd5b506103c861095a366004612561565b611a06565b34801561096b57600080fd5b50600554600160801b900463ffffffff166104dd565b34801561098d57600080fd5b506009546102f990600160d01b900463ffffffff1681565b3480156109b157600080fd5b506103c86109c0366004612537565b611a81565b600181815481106109d557600080fd5b6000918252602090912001546001600160a01b0316905081565b620151808163ffffffff161015610a1957604051633a50e6a160e01b815260040160405180910390fd5b620697808163ffffffff161115610a43576040516317b7efeb60e11b815260040160405180910390fd5b50565b610a4e611ab5565b610e108263ffffffff161015610a7757604051639a4e9a8d60e01b815260040160405180910390fd5b6154608163ffffffff161115610aa0576040516310c4878160e11b815260040160405180910390fd5b600580546fffffffffffffffff00000000000000001916600160401b63ffffffff85811691820263ffffffff60601b191692909217600160601b928516928302179092556040805192835260208301919091527f47c8a5b15f6172a5053bfac488642484dd38e9c0f3073c23c5d822c4cbab530991015b60405180910390a15050565b610b2b611ab5565b60098054821515600160a01b0260ff60a01b199091161790556040517f8dd94cb9d51a52430f03387ae2b623450f5bbb1201ce3d08f9dc3561f258cf4190610b7890831515815260200190565b60405180910390a150565b610b8b611ab5565b6276a7008263ffffffff161115610bb55760405163766cee2b60e01b815260040160405180910390fd5b6154608163ffffffff161015610bde57604051638750566960e01b815260040160405180910390fd5b6283d6008163ffffffff161115610c08576040516319979c0960e11b815260040160405180910390fd5b6005805467ffffffffffffffff60801b1916600160a01b63ffffffff85811691820263ffffffff60801b191692909217600160801b928516928302179092556040805192835260208301919091527f7b8aa35646b8fddc3ccaae8e1069d8c6c365e2ba2e11bb1f0018f8d01992f25b9101610b17565b610c86611ab5565b610c8f816109ef565b6009805463ffffffff60b01b1916600160b01b63ffffffff8416908102919091179091556040519081527fc14f60dbf7139b2e92ad845e95c05208c6070962e410202d3b8ae7a1378cde3590602001610b78565b610ceb611ab5565b60068190556040518181527f13fa675fefcae94b0250d92c1fec53cd08f7031592b28bf9429f840b7d25509790602001610b78565b6000610d4b7f0000000000000000000000000000000000000000000000000000000000000000611b0f565b60405163be9efac960e01b81529091506001600160a01b0382169063be9efac990610d7a90859060040161283f565b600060405180830381600087803b158015610d9457600080fd5b505af1158015610da8573d6000803e3d6000fd5b50506001805480820182556000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b0319166001600160a01b0385169081179091556040519092507f87c42456973803b7e30e88a3d384868c760496c823f4eb51223d3bc094b8aa1d9150610e2b90859061283f565b60405180910390a2919050565b610e40611ab5565b62ed4e008263ffffffff1610610e695760405163181b6a8d60e01b815260040160405180910390fd5b8063ffffffff16600003610e905760405163497a0fc360e01b815260040160405180910390fd5b8063ffffffff168263ffffffff161015610ebd576040516301a127dd60e51b815260040160405180910390fd5b6005805463ffffffff84811667ffffffffffffffff199092168217640100000000918516918202179092556040805191825260208201929092527fa3d5527033c65a7e5eda0ffec2fe75b8bfc358a419914452e5e29993f531136d9101610b17565b6001600160a01b03808516600090815260036020908152604080832093871683529290529081208054849290610f569084906129e4565b90915550506001600160a01b03841660009081526004602052604081208054839290610f839084906129e4565b90915550610faa90503330610f9884866129e4565b6001600160a01b038816929190611ba9565b50505050565b610fb8611ab5565b6202a3008163ffffffff161015610fe25760405163514e00f160e01b815260040160405180910390fd5b6005805463ffffffff60c01b1916600160c01b63ffffffff8416908102919091179091556040519081527fc2f118f047950efbfb94865ef4fc99eedc6b5e992e4fb970552deba4fda0d8e490602001610b78565b61103e611ab5565b6110486000611c14565b565b6202a3008163ffffffff16101561107457604051630fe180cd60e01b815260040160405180910390fd5b62700f808163ffffffff161115610a435760405163599dfd5d60e11b815260040160405180910390fd5b6110a6611ab5565b6110b08282611a81565b6008805463ffffffff60a01b1916600160a01b61ffff85811691820261ffff60b01b191692909217600160b01b928516928302179092556040805192835260208301919091527f9b9cfb21b737a43af5e89fc41ea51449648ce863399879a0625210588b164e5a9101610b17565b611126611ab5565b60098054821515600160a81b0260ff60a81b199091161790556040517fa18d588b4da0466ca9a3ade6753c1cf94f4ccc599b9134045aeb55aef53293c890610b7890831515815260200190565b61117b611ab5565b6111848161104a565b6009805463ffffffff60d01b1916600160d01b63ffffffff8416908102919091179091556040519081527fe6f3ddadc73ca32beb7d9755d5a7d82ab53101c097e743fd05412e6a1ce9deb990602001610b78565b6111e0611ab5565b600980546001600160a01b0319166001600160a01b0383169081179091556040517f4664bc54da0879a6626395d3154ebb4adef21b4ced228635ce9583e86148a9a790600090a250565b611232611ab5565b6001600160a01b038216600081815260026020526040808220805460ff191685151590811790915590519092917f0f8de79a0a8e021d06491203f918550c01da39c26f2e03c5f5571c75edb7d93791a35050565b61128e611ab5565b600880546001600160a01b0319166001600160a01b0383169081179091556040517f9993507f53024702e2de1be66f6c48c8957ac708927b86b3bdf4e335f586947090600090a250565b6112e0611ab5565b600780546001600160a01b0319166001600160a01b0383169081179091556040517f611a318453985b405b2f5e92971f8c383619cf48b7bf4acf39e83ed1e111180590600090a250565b611332611ab5565b61137a60405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016060815260200160008152602001600081525090565b8567ffffffffffffffff811115611393576113936129fd565b6040519080825280602002602001820160405280156113bc578160200160208202803683370190505b5060a08201526001600160a01b03881660009081526004602052604081205460c0830181905282525b86811015611508576001600160a01b03891660009081526003602052604081209089898481811061141857611418612a13565b905060200201602081019061142d9190612561565b6001600160a01b03166001600160a01b03168152602001908152602001600020548260a00151828151811061146457611464612a13565b6020908102919091018101919091526001600160a01b038a166000908152600390915260408120818a8a8581811061149e5761149e612a13565b90506020020160208101906114b39190612561565b6001600160a01b0316815260208101919091526040016000205560a08201518051829081106114e4576114e4612a13565b6020026020010151826000018181516114fd91906129e4565b9052506001016113e5565b50805160000361152b5760405163dbe66a0f60e01b815260040160405180910390fd5b60075481516001600160a01b039091169061154b908a9088888888611c64565b60208401819052604084019190915282516115669190612a29565b60e0830181905260c0830151835161157f929190611f38565b608083015260c08201518251602084015161159b929091611f38565b6001600160a01b038a166000908152600460205260408120919091555b878110156118895760006115fc8460a0015183815181106115db576115db612a13565b602002602001015185600001518660400151611f389092919063ffffffff16565b9050600061163a8560a00151848151811061161957611619612a13565b602002602001015186600001518760e00151611f389092919063ffffffff16565b9050818560600181815161164e91906129e4565b90525060a0850151805161168e91908590811061166d5761166d612a13565b602002602001015186600001518760200151611f389092919063ffffffff16565b6001600160a01b038d166000908152600360205260408120908d8d878181106116b9576116b9612a13565b90506020020160208101906116ce9190612561565b6001600160a01b03168152602081019190915260400160009081209190915582156117ff577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e95cd0518660008f8f8981811061173757611737612a13565b905060200201602081019061174c9190612561565b600554889042906117639063ffffffff16826129e4565b60055460405160e089901b6001600160e01b03191681526117a697969594939291640100000000900463ffffffff16906000908190600290600190600401612a3c565b6020604051808303816000875af11580156117c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e99190612ad1565b90506117ff6001600160a01b0386168285611fed565b806001600160a01b03168c8c8681811061181b5761181b612a13565b90506020020160208101906118309190612561565b6001600160a01b03167f4f8abed31d725d1e727a30b06defad85513b99ee0d373925f63d71e099edaa308486604051611873929190918252602082015260400190565b60405180910390a38360010193505050506115b8565b50600080546001600160a01b031690506000836060015184604001516118af9190612a29565b90506118c56001600160a01b0384168383611fed565b608084015160408051918252602082018390526000916001600160a01b038516917f4f8abed31d725d1e727a30b06defad85513b99ee0d373925f63d71e099edaa30910160405180910390a35050505050505050505050565b60065480156119be578034101561194857604051633244470d60e01b815260040160405180910390fd5b600080546040516001600160a01b039091169034908381818185875af1925050503d8060008114611995576040519150601f19603f3d011682016040523d82523d6000602084013e61199a565b606091505b50509050806119bc5760405163684fb74f60e11b815260040160405180910390fd5b505b336001600160a01b03167f9fcc410c642f895d8e83453cc710a30c7f00e42070fb7fd399c29b24781615e884846040516119f9929190612aee565b60405180910390a2505050565b611a0e611ab5565b6001600160a01b038116611a785760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b610a4381611c14565b6107d0611a8e8284612b02565b61ffff161115611ab1576040516307af556960e21b815260040160405180910390fd5b5050565b6000546001600160a01b031633146110485760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401611a6f565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c176000526e5af43d82803e903d91602b57fd5bf38260781b17602052603760096000f090506001600160a01b038116611ba45760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b6044820152606401611a6f565b919050565b6040516001600160a01b0380851660248301528316604482015260648101829052610faa9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612022565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60075460009081906001600160a01b03908116908916819003611c8e578760009250925050611f2d565b611ca26001600160a01b038a16878a6120f4565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611ce9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d0d9190612b24565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038c16906370a0823190602401602060405180830381865afa158015611d57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7b9190612b24565b90506000886001600160a01b03168888604051611d99929190612b3d565b6000604051808303816000865af19150503d8060008114611dd6576040519150601f19603f3d011682016040523d82523d6000602084013e611ddb565b606091505b5050905080611dfd5760405163081ceff360e41b815260040160405180910390fd5b6040516370a0823160e01b815230600482015283906001600160a01b038616906370a0823190602401602060405180830381865afa158015611e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e679190612b24565b611e719190612a29565b6040516370a0823160e01b81523060048201529096506001600160a01b038d16906370a0823190602401602060405180830381865afa158015611eb8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611edc9190612b24565b611ee69083612a29565b611ef0908c612a29565b945089861015611f1357604051638a866e2560e01b815260040160405180910390fd5b611f286001600160a01b038d168a60006120f4565b505050505b965096945050505050565b6000808060001985870985870292508281108382030391505080600003611f7257838281611f6857611f68612b4d565b0492505050611fe6565b808411611f7e57600080fd5b600084868809851960019081018716968790049682860381900495909211909303600082900391909104909201919091029190911760038402600290811880860282030280860282030280860282030280860282030280860282030280860290910302029150505b9392505050565b6040516001600160a01b03831660248201526044810182905261201d90849063a9059cbb60e01b90606401611bdd565b505050565b6000612077826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166122099092919063ffffffff16565b80519091501561201d57808060200190518101906120959190612b63565b61201d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401611a6f565b80158061216e5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612148573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216c9190612b24565b155b6121d95760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401611a6f565b6040516001600160a01b03831660248201526044810182905261201d90849063095ea7b360e01b90606401611bdd565b60606122188484600085612220565b949350505050565b6060824710156122815760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401611a6f565b600080866001600160a01b0316858760405161229d9190612ba4565b60006040518083038185875af1925050503d80600081146122da576040519150601f19603f3d011682016040523d82523d6000602084013e6122df565b606091505b50915091506122f0878383876122fb565b979650505050505050565b6060831561236a578251600003612363576001600160a01b0385163b6123635760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611a6f565b5081612218565b612218838381511561237f5781518083602001fd5b8060405162461bcd60e51b8152600401611a6f9190612bc0565b6000602082840312156123ab57600080fd5b5035919050565b803563ffffffff81168114611ba457600080fd5b6000602082840312156123d857600080fd5b611fe6826123b2565b600080604083850312156123f457600080fd5b6123fd836123b2565b915061240b602084016123b2565b90509250929050565b8015158114610a4357600080fd5b8035611ba481612414565b60006020828403121561243f57600080fd5b8135611fe681612414565b6001600160a01b0381168114610a4357600080fd5b8035611ba48161244a565b6000806040838503121561247d57600080fd5b82356124888161244a565b915060208301356124988161244a565b809150509250929050565b6000602082840312156124b557600080fd5b813567ffffffffffffffff8111156124cc57600080fd5b82016101c08185031215611fe657600080fd5b600080600080608085870312156124f557600080fd5b84356125008161244a565b935060208501356125108161244a565b93969395505050506040820135916060013590565b803561ffff81168114611ba457600080fd5b6000806040838503121561254a57600080fd5b61255383612525565b915061240b60208401612525565b60006020828403121561257357600080fd5b8135611fe68161244a565b6000806040838503121561259157600080fd5b823561259c8161244a565b9150602083013561249881612414565b60008083601f8401126125be57600080fd5b50813567ffffffffffffffff8111156125d657600080fd5b6020830191508360208285010111156125ee57600080fd5b9250929050565b600080600080600080600060a0888a03121561261057600080fd5b873561261b8161244a565b9650602088013567ffffffffffffffff8082111561263857600080fd5b818a0191508a601f83011261264c57600080fd5b81358181111561265b57600080fd5b8b60208260051b850101111561267057600080fd5b602083019850965060408a0135955061268b60608b0161245f565b945060808a01359150808211156126a157600080fd5b506126ae8a828b016125ac565b989b979a50959850939692959293505050565b600080602083850312156126d457600080fd5b823567ffffffffffffffff8111156126eb57600080fd5b6126f7858286016125ac565b90969095509350505050565b6000808335601e1984360301811261271a57600080fd5b830160208101925035905067ffffffffffffffff81111561273a57600080fd5b8036038213156125ee57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000808335601e1984360301811261278957600080fd5b830160208101925035905067ffffffffffffffff8111156127a957600080fd5b8060051b36038213156125ee57600080fd5b8183526000602080850194508260005b858110156127f95781356127de8161244a565b6001600160a01b0316875295820195908201906001016127cb565b509495945050505050565b61ffff8061281183612525565b1683528061282160208401612525565b1660208401528061283460408401612525565b166040840152505050565b60208152600061284f8384612703565b6101c08060208601526128676101e086018385612749565b92506128766020870187612703565b9250601f1980878603016040880152612890858584612749565b945061289f6040890189612772565b94509150808786030160608801526128b88585846127bb565b94506128c6606089016123b2565b63ffffffff8116608089015293506128e0608089016123b2565b63ffffffff811660a089015293506128fa60a08901612525565b61ffff811660c0890152935061291660e0880160c08a01612804565b612923610120890161245f565b9350610140915061293e828801856001600160a01b03169052565b61294982890161245f565b93506101609150612964828801856001600160a01b03169052565b61296f82890161245f565b9350610180915061298a828801856001600160a01b03169052565b612995828901612422565b93506101a091506129a98288018515159052565b6129b582890189612703565b94509150808786030183880152506122f0848483612749565b634e487b7160e01b600052601160045260246000fd5b808201808211156129f7576129f76129ce565b92915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b818103818111156129f7576129f76129ce565b6001600160a01b038c811682528b811660208301528a166040820152606081018990526080810188905260a0810187905263ffffffff861660c082015260e081018590526101008101849052610160810160038410612aab57634e487b7160e01b600052602160045260246000fd5b83610120830152612ac161014083018415159052565b9c9b505050505050505050505050565b600060208284031215612ae357600080fd5b8151611fe68161244a565b602081526000612218602083018486612749565b61ffff818116838216019080821115612b1d57612b1d6129ce565b5092915050565b600060208284031215612b3657600080fd5b5051919050565b8183823760009101908152919050565b634e487b7160e01b600052601260045260246000fd5b600060208284031215612b7557600080fd5b8151611fe681612414565b60005b83811015612b9b578181015183820152602001612b83565b50506000910152565b60008251612bb6818460208701612b80565b9190910192915050565b6020815260008251806020840152612bdf816040850160208701612b80565b601f01601f1916919091016040019291505056fea264697066735822122008e20e885bcbd634116756388a3314073f7d027fb51e915cb131d91c5a80dd8c64736f6c63430008100033000000000000000000000000028a7c6873dfa8357c9dcf9c9d76ef2abb66256e00000000000000000000000007368f6a959ef3096230a258dd0af692699c3a4c000000000000000000000000cbe0b90bfe99f827b8bcb5c5ac4b17107caea814000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e198cbb727758b9ad38a12e1ad475a843e5e730f

Deployed Bytecode

0x6080604052600436106102935760003560e01c806377c9cd8c1161015a578063d3e0bc56116100c1578063ea55f18e1161007a578063ea55f18e14610907578063eeb338871461091d578063f2fde38b1461093f578063f4bde0971461095f578063f51a90e414610981578063fb9a8740146109a557600080fd5b8063d3e0bc56146107bf578063db0f267b146107d4578063db399053146107f4578063df1b1ebe14610824578063e2b001bf146108d4578063e367497d146108f457600080fd5b8063a3fe95b211610113578063a3fe95b2146106f2578063a8acd94614610712578063aec1d10814610732578063b19805af14610752578063b851b7ca14610772578063bcfdfc581461079257600080fd5b806377c9cd8c1461063257806387cf3ef414610654578063891ce467146106745780638da5cb5b1461069457806392b2fd34146106b25780639d0ab89f146106d257600080fd5b80632e75ab50116101fe5780634f9627b0116101b75780634f9627b01461058557806354492078146105a55780635dd11415146105c75780636c0d189a146105e75780636ed93dd014610607578063715018a61461061d57600080fd5b80632e75ab501461049f5780632ea7f250146104bf5780633449a865146104eb578063354d75b91461050b5780633bd495ce14610543578063403661fb1461056557600080fd5b80631a95cf06116102505780631a95cf06146103ca5780631b2cae6c146103ea57806321c0437a1461040a5780632350e92d1461043e578063250c2cd91461045e578063290d10c41461047e57600080fd5b806306c6e3b81461029857806311566d74146102d5578063116dbc931461030e57806311c2aae31461033f578063120c1a7e14610373578063165db9a9146103a8575b600080fd5b3480156102a457600080fd5b506102b86102b3366004612399565b6109c5565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102e157600080fd5b506009546102f990600160b01b900463ffffffff1681565b60405163ffffffff90911681526020016102cc565b34801561031a57600080fd5b5060095461032f90600160a01b900460ff1681565b60405190151581526020016102cc565b34801561034b57600080fd5b506102b87f000000000000000000000000e198cbb727758b9ad38a12e1ad475a843e5e730f81565b34801561037f57600080fd5b5060085461039590600160b01b900461ffff1681565b60405161ffff90911681526020016102cc565b3480156103b457600080fd5b506103c86103c33660046123c6565b6109ef565b005b3480156103d657600080fd5b506103c86103e53660046123e1565b610a46565b3480156103f657600080fd5b506103c861040536600461242d565b610b23565b34801561041657600080fd5b506102b87f000000000000000000000000028a7c6873dfa8357c9dcf9c9d76ef2abb66256e81565b34801561044a57600080fd5b506103c86104593660046123e1565b610b83565b34801561046a57600080fd5b506103c86104793660046123c6565b610c7e565b34801561048a57600080fd5b5060095461032f90600160a81b900460ff1681565b3480156104ab57600080fd5b506103c86104ba366004612399565b610ce3565b3480156104cb57600080fd5b50600554600160401b900463ffffffff165b6040519081526020016102cc565b3480156104f757600080fd5b506007546102b8906001600160a01b031681565b34801561051757600080fd5b506104dd61052636600461246a565b600360209081526000928352604080842090915290825290205481565b34801561054f57600080fd5b50600554600160a01b900463ffffffff166104dd565b34801561057157600080fd5b506102b86105803660046124a3565b610d20565b34801561059157600080fd5b506103c86105a03660046123e1565b610e38565b3480156105b157600080fd5b5060085461039590600160a01b900461ffff1681565b3480156105d357600080fd5b506103c86105e23660046124df565b610f1f565b3480156105f357600080fd5b506103c86106023660046123c6565b610fb0565b34801561061357600080fd5b5061039561271081565b34801561062957600080fd5b506103c8611036565b34801561063e57600080fd5b50600554600160c01b900463ffffffff166104dd565b34801561066057600080fd5b506008546102b8906001600160a01b031681565b34801561068057600080fd5b506103c861068f3660046123c6565b61104a565b3480156106a057600080fd5b506000546001600160a01b03166102b8565b3480156106be57600080fd5b506103c86106cd366004612537565b61109e565b3480156106de57600080fd5b506103c86106ed36600461242d565b61111e565b3480156106fe57600080fd5b506103c861070d3660046123c6565b611173565b34801561071e57600080fd5b506103c861072d366004612561565b6111d8565b34801561073e57600080fd5b506103c861074d36600461257e565b61122a565b34801561075e57600080fd5b506103c861076d366004612561565b611286565b34801561077e57600080fd5b506103c861078d366004612561565b6112d8565b34801561079e57600080fd5b506104dd6107ad366004612561565b60046020526000908152604090205481565b3480156107cb57600080fd5b506001546104dd565b3480156107e057600080fd5b506009546102b8906001600160a01b031681565b34801561080057600080fd5b5061032f61080f366004612561565b60026020526000908152604090205460ff1681565b34801561083057600080fd5b506005546006546108849163ffffffff808216926401000000008304821692600160401b8104831692600160601b8204811692600160801b8304821692600160a01b8104831692600160c01b909104169088565b6040805163ffffffff998a16815297891660208901529588169587019590955292861660608601529085166080850152841660a084015290921660c082015260e0810191909152610100016102cc565b3480156108e057600080fd5b506103c86108ef3660046125f5565b61132a565b6103c86109023660046126c1565b61191e565b34801561091357600080fd5b506103956107d081565b34801561092957600080fd5b50600554600160601b900463ffffffff166104dd565b34801561094b57600080fd5b506103c861095a366004612561565b611a06565b34801561096b57600080fd5b50600554600160801b900463ffffffff166104dd565b34801561098d57600080fd5b506009546102f990600160d01b900463ffffffff1681565b3480156109b157600080fd5b506103c86109c0366004612537565b611a81565b600181815481106109d557600080fd5b6000918252602090912001546001600160a01b0316905081565b620151808163ffffffff161015610a1957604051633a50e6a160e01b815260040160405180910390fd5b620697808163ffffffff161115610a43576040516317b7efeb60e11b815260040160405180910390fd5b50565b610a4e611ab5565b610e108263ffffffff161015610a7757604051639a4e9a8d60e01b815260040160405180910390fd5b6154608163ffffffff161115610aa0576040516310c4878160e11b815260040160405180910390fd5b600580546fffffffffffffffff00000000000000001916600160401b63ffffffff85811691820263ffffffff60601b191692909217600160601b928516928302179092556040805192835260208301919091527f47c8a5b15f6172a5053bfac488642484dd38e9c0f3073c23c5d822c4cbab530991015b60405180910390a15050565b610b2b611ab5565b60098054821515600160a01b0260ff60a01b199091161790556040517f8dd94cb9d51a52430f03387ae2b623450f5bbb1201ce3d08f9dc3561f258cf4190610b7890831515815260200190565b60405180910390a150565b610b8b611ab5565b6276a7008263ffffffff161115610bb55760405163766cee2b60e01b815260040160405180910390fd5b6154608163ffffffff161015610bde57604051638750566960e01b815260040160405180910390fd5b6283d6008163ffffffff161115610c08576040516319979c0960e11b815260040160405180910390fd5b6005805467ffffffffffffffff60801b1916600160a01b63ffffffff85811691820263ffffffff60801b191692909217600160801b928516928302179092556040805192835260208301919091527f7b8aa35646b8fddc3ccaae8e1069d8c6c365e2ba2e11bb1f0018f8d01992f25b9101610b17565b610c86611ab5565b610c8f816109ef565b6009805463ffffffff60b01b1916600160b01b63ffffffff8416908102919091179091556040519081527fc14f60dbf7139b2e92ad845e95c05208c6070962e410202d3b8ae7a1378cde3590602001610b78565b610ceb611ab5565b60068190556040518181527f13fa675fefcae94b0250d92c1fec53cd08f7031592b28bf9429f840b7d25509790602001610b78565b6000610d4b7f000000000000000000000000028a7c6873dfa8357c9dcf9c9d76ef2abb66256e611b0f565b60405163be9efac960e01b81529091506001600160a01b0382169063be9efac990610d7a90859060040161283f565b600060405180830381600087803b158015610d9457600080fd5b505af1158015610da8573d6000803e3d6000fd5b50506001805480820182556000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b0319166001600160a01b0385169081179091556040519092507f87c42456973803b7e30e88a3d384868c760496c823f4eb51223d3bc094b8aa1d9150610e2b90859061283f565b60405180910390a2919050565b610e40611ab5565b62ed4e008263ffffffff1610610e695760405163181b6a8d60e01b815260040160405180910390fd5b8063ffffffff16600003610e905760405163497a0fc360e01b815260040160405180910390fd5b8063ffffffff168263ffffffff161015610ebd576040516301a127dd60e51b815260040160405180910390fd5b6005805463ffffffff84811667ffffffffffffffff199092168217640100000000918516918202179092556040805191825260208201929092527fa3d5527033c65a7e5eda0ffec2fe75b8bfc358a419914452e5e29993f531136d9101610b17565b6001600160a01b03808516600090815260036020908152604080832093871683529290529081208054849290610f569084906129e4565b90915550506001600160a01b03841660009081526004602052604081208054839290610f839084906129e4565b90915550610faa90503330610f9884866129e4565b6001600160a01b038816929190611ba9565b50505050565b610fb8611ab5565b6202a3008163ffffffff161015610fe25760405163514e00f160e01b815260040160405180910390fd5b6005805463ffffffff60c01b1916600160c01b63ffffffff8416908102919091179091556040519081527fc2f118f047950efbfb94865ef4fc99eedc6b5e992e4fb970552deba4fda0d8e490602001610b78565b61103e611ab5565b6110486000611c14565b565b6202a3008163ffffffff16101561107457604051630fe180cd60e01b815260040160405180910390fd5b62700f808163ffffffff161115610a435760405163599dfd5d60e11b815260040160405180910390fd5b6110a6611ab5565b6110b08282611a81565b6008805463ffffffff60a01b1916600160a01b61ffff85811691820261ffff60b01b191692909217600160b01b928516928302179092556040805192835260208301919091527f9b9cfb21b737a43af5e89fc41ea51449648ce863399879a0625210588b164e5a9101610b17565b611126611ab5565b60098054821515600160a81b0260ff60a81b199091161790556040517fa18d588b4da0466ca9a3ade6753c1cf94f4ccc599b9134045aeb55aef53293c890610b7890831515815260200190565b61117b611ab5565b6111848161104a565b6009805463ffffffff60d01b1916600160d01b63ffffffff8416908102919091179091556040519081527fe6f3ddadc73ca32beb7d9755d5a7d82ab53101c097e743fd05412e6a1ce9deb990602001610b78565b6111e0611ab5565b600980546001600160a01b0319166001600160a01b0383169081179091556040517f4664bc54da0879a6626395d3154ebb4adef21b4ced228635ce9583e86148a9a790600090a250565b611232611ab5565b6001600160a01b038216600081815260026020526040808220805460ff191685151590811790915590519092917f0f8de79a0a8e021d06491203f918550c01da39c26f2e03c5f5571c75edb7d93791a35050565b61128e611ab5565b600880546001600160a01b0319166001600160a01b0383169081179091556040517f9993507f53024702e2de1be66f6c48c8957ac708927b86b3bdf4e335f586947090600090a250565b6112e0611ab5565b600780546001600160a01b0319166001600160a01b0383169081179091556040517f611a318453985b405b2f5e92971f8c383619cf48b7bf4acf39e83ed1e111180590600090a250565b611332611ab5565b61137a60405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016060815260200160008152602001600081525090565b8567ffffffffffffffff811115611393576113936129fd565b6040519080825280602002602001820160405280156113bc578160200160208202803683370190505b5060a08201526001600160a01b03881660009081526004602052604081205460c0830181905282525b86811015611508576001600160a01b03891660009081526003602052604081209089898481811061141857611418612a13565b905060200201602081019061142d9190612561565b6001600160a01b03166001600160a01b03168152602001908152602001600020548260a00151828151811061146457611464612a13565b6020908102919091018101919091526001600160a01b038a166000908152600390915260408120818a8a8581811061149e5761149e612a13565b90506020020160208101906114b39190612561565b6001600160a01b0316815260208101919091526040016000205560a08201518051829081106114e4576114e4612a13565b6020026020010151826000018181516114fd91906129e4565b9052506001016113e5565b50805160000361152b5760405163dbe66a0f60e01b815260040160405180910390fd5b60075481516001600160a01b039091169061154b908a9088888888611c64565b60208401819052604084019190915282516115669190612a29565b60e0830181905260c0830151835161157f929190611f38565b608083015260c08201518251602084015161159b929091611f38565b6001600160a01b038a166000908152600460205260408120919091555b878110156118895760006115fc8460a0015183815181106115db576115db612a13565b602002602001015185600001518660400151611f389092919063ffffffff16565b9050600061163a8560a00151848151811061161957611619612a13565b602002602001015186600001518760e00151611f389092919063ffffffff16565b9050818560600181815161164e91906129e4565b90525060a0850151805161168e91908590811061166d5761166d612a13565b602002602001015186600001518760200151611f389092919063ffffffff16565b6001600160a01b038d166000908152600360205260408120908d8d878181106116b9576116b9612a13565b90506020020160208101906116ce9190612561565b6001600160a01b03168152602081019190915260400160009081209190915582156117ff577f000000000000000000000000e198cbb727758b9ad38a12e1ad475a843e5e730f6001600160a01b031663e95cd0518660008f8f8981811061173757611737612a13565b905060200201602081019061174c9190612561565b600554889042906117639063ffffffff16826129e4565b60055460405160e089901b6001600160e01b03191681526117a697969594939291640100000000900463ffffffff16906000908190600290600190600401612a3c565b6020604051808303816000875af11580156117c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e99190612ad1565b90506117ff6001600160a01b0386168285611fed565b806001600160a01b03168c8c8681811061181b5761181b612a13565b90506020020160208101906118309190612561565b6001600160a01b03167f4f8abed31d725d1e727a30b06defad85513b99ee0d373925f63d71e099edaa308486604051611873929190918252602082015260400190565b60405180910390a38360010193505050506115b8565b50600080546001600160a01b031690506000836060015184604001516118af9190612a29565b90506118c56001600160a01b0384168383611fed565b608084015160408051918252602082018390526000916001600160a01b038516917f4f8abed31d725d1e727a30b06defad85513b99ee0d373925f63d71e099edaa30910160405180910390a35050505050505050505050565b60065480156119be578034101561194857604051633244470d60e01b815260040160405180910390fd5b600080546040516001600160a01b039091169034908381818185875af1925050503d8060008114611995576040519150601f19603f3d011682016040523d82523d6000602084013e61199a565b606091505b50509050806119bc5760405163684fb74f60e11b815260040160405180910390fd5b505b336001600160a01b03167f9fcc410c642f895d8e83453cc710a30c7f00e42070fb7fd399c29b24781615e884846040516119f9929190612aee565b60405180910390a2505050565b611a0e611ab5565b6001600160a01b038116611a785760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b610a4381611c14565b6107d0611a8e8284612b02565b61ffff161115611ab1576040516307af556960e21b815260040160405180910390fd5b5050565b6000546001600160a01b031633146110485760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401611a6f565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c176000526e5af43d82803e903d91602b57fd5bf38260781b17602052603760096000f090506001600160a01b038116611ba45760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b6044820152606401611a6f565b919050565b6040516001600160a01b0380851660248301528316604482015260648101829052610faa9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612022565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60075460009081906001600160a01b03908116908916819003611c8e578760009250925050611f2d565b611ca26001600160a01b038a16878a6120f4565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611ce9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d0d9190612b24565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038c16906370a0823190602401602060405180830381865afa158015611d57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7b9190612b24565b90506000886001600160a01b03168888604051611d99929190612b3d565b6000604051808303816000865af19150503d8060008114611dd6576040519150601f19603f3d011682016040523d82523d6000602084013e611ddb565b606091505b5050905080611dfd5760405163081ceff360e41b815260040160405180910390fd5b6040516370a0823160e01b815230600482015283906001600160a01b038616906370a0823190602401602060405180830381865afa158015611e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e679190612b24565b611e719190612a29565b6040516370a0823160e01b81523060048201529096506001600160a01b038d16906370a0823190602401602060405180830381865afa158015611eb8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611edc9190612b24565b611ee69083612a29565b611ef0908c612a29565b945089861015611f1357604051638a866e2560e01b815260040160405180910390fd5b611f286001600160a01b038d168a60006120f4565b505050505b965096945050505050565b6000808060001985870985870292508281108382030391505080600003611f7257838281611f6857611f68612b4d565b0492505050611fe6565b808411611f7e57600080fd5b600084868809851960019081018716968790049682860381900495909211909303600082900391909104909201919091029190911760038402600290811880860282030280860282030280860282030280860282030280860282030280860290910302029150505b9392505050565b6040516001600160a01b03831660248201526044810182905261201d90849063a9059cbb60e01b90606401611bdd565b505050565b6000612077826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166122099092919063ffffffff16565b80519091501561201d57808060200190518101906120959190612b63565b61201d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401611a6f565b80158061216e5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612148573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216c9190612b24565b155b6121d95760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401611a6f565b6040516001600160a01b03831660248201526044810182905261201d90849063095ea7b360e01b90606401611bdd565b60606122188484600085612220565b949350505050565b6060824710156122815760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401611a6f565b600080866001600160a01b0316858760405161229d9190612ba4565b60006040518083038185875af1925050503d80600081146122da576040519150601f19603f3d011682016040523d82523d6000602084013e6122df565b606091505b50915091506122f0878383876122fb565b979650505050505050565b6060831561236a578251600003612363576001600160a01b0385163b6123635760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611a6f565b5081612218565b612218838381511561237f5781518083602001fd5b8060405162461bcd60e51b8152600401611a6f9190612bc0565b6000602082840312156123ab57600080fd5b5035919050565b803563ffffffff81168114611ba457600080fd5b6000602082840312156123d857600080fd5b611fe6826123b2565b600080604083850312156123f457600080fd5b6123fd836123b2565b915061240b602084016123b2565b90509250929050565b8015158114610a4357600080fd5b8035611ba481612414565b60006020828403121561243f57600080fd5b8135611fe681612414565b6001600160a01b0381168114610a4357600080fd5b8035611ba48161244a565b6000806040838503121561247d57600080fd5b82356124888161244a565b915060208301356124988161244a565b809150509250929050565b6000602082840312156124b557600080fd5b813567ffffffffffffffff8111156124cc57600080fd5b82016101c08185031215611fe657600080fd5b600080600080608085870312156124f557600080fd5b84356125008161244a565b935060208501356125108161244a565b93969395505050506040820135916060013590565b803561ffff81168114611ba457600080fd5b6000806040838503121561254a57600080fd5b61255383612525565b915061240b60208401612525565b60006020828403121561257357600080fd5b8135611fe68161244a565b6000806040838503121561259157600080fd5b823561259c8161244a565b9150602083013561249881612414565b60008083601f8401126125be57600080fd5b50813567ffffffffffffffff8111156125d657600080fd5b6020830191508360208285010111156125ee57600080fd5b9250929050565b600080600080600080600060a0888a03121561261057600080fd5b873561261b8161244a565b9650602088013567ffffffffffffffff8082111561263857600080fd5b818a0191508a601f83011261264c57600080fd5b81358181111561265b57600080fd5b8b60208260051b850101111561267057600080fd5b602083019850965060408a0135955061268b60608b0161245f565b945060808a01359150808211156126a157600080fd5b506126ae8a828b016125ac565b989b979a50959850939692959293505050565b600080602083850312156126d457600080fd5b823567ffffffffffffffff8111156126eb57600080fd5b6126f7858286016125ac565b90969095509350505050565b6000808335601e1984360301811261271a57600080fd5b830160208101925035905067ffffffffffffffff81111561273a57600080fd5b8036038213156125ee57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000808335601e1984360301811261278957600080fd5b830160208101925035905067ffffffffffffffff8111156127a957600080fd5b8060051b36038213156125ee57600080fd5b8183526000602080850194508260005b858110156127f95781356127de8161244a565b6001600160a01b0316875295820195908201906001016127cb565b509495945050505050565b61ffff8061281183612525565b1683528061282160208401612525565b1660208401528061283460408401612525565b166040840152505050565b60208152600061284f8384612703565b6101c08060208601526128676101e086018385612749565b92506128766020870187612703565b9250601f1980878603016040880152612890858584612749565b945061289f6040890189612772565b94509150808786030160608801526128b88585846127bb565b94506128c6606089016123b2565b63ffffffff8116608089015293506128e0608089016123b2565b63ffffffff811660a089015293506128fa60a08901612525565b61ffff811660c0890152935061291660e0880160c08a01612804565b612923610120890161245f565b9350610140915061293e828801856001600160a01b03169052565b61294982890161245f565b93506101609150612964828801856001600160a01b03169052565b61296f82890161245f565b9350610180915061298a828801856001600160a01b03169052565b612995828901612422565b93506101a091506129a98288018515159052565b6129b582890189612703565b94509150808786030183880152506122f0848483612749565b634e487b7160e01b600052601160045260246000fd5b808201808211156129f7576129f76129ce565b92915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b818103818111156129f7576129f76129ce565b6001600160a01b038c811682528b811660208301528a166040820152606081018990526080810188905260a0810187905263ffffffff861660c082015260e081018590526101008101849052610160810160038410612aab57634e487b7160e01b600052602160045260246000fd5b83610120830152612ac161014083018415159052565b9c9b505050505050505050505050565b600060208284031215612ae357600080fd5b8151611fe68161244a565b602081526000612218602083018486612749565b61ffff818116838216019080821115612b1d57612b1d6129ce565b5092915050565b600060208284031215612b3657600080fd5b5051919050565b8183823760009101908152919050565b634e487b7160e01b600052601260045260246000fd5b600060208284031215612b7557600080fd5b8151611fe681612414565b60005b83811015612b9b578181015183820152602001612b83565b50506000910152565b60008251612bb6818460208701612b80565b9190910192915050565b6020815260008251806020840152612bdf816040850160208701612b80565b601f01601f1916919091016040019291505056fea264697066735822122008e20e885bcbd634116756388a3314073f7d027fb51e915cb131d91c5a80dd8c64736f6c63430008100033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000028a7c6873dfa8357c9dcf9c9d76ef2abb66256e00000000000000000000000007368f6a959ef3096230a258dd0af692699c3a4c000000000000000000000000cbe0b90bfe99f827b8bcb5c5ac4b17107caea814000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e198cbb727758b9ad38a12e1ad475a843e5e730f

-----Decoded View---------------
Arg [0] : _hatVaultImplementation (address): 0x028A7C6873dFA8357c9dcF9C9d76EF2abb66256E
Arg [1] : _hatGovernance (address): 0x07368F6a959Ef3096230a258dd0af692699c3a4c
Arg [2] : _defaultArbitrator (address): 0xcBe0b90bfe99f827B8BCB5C5Ac4b17107caEA814
Arg [3] : _HAT (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [4] : _bountyGovernanceHAT (uint16): 1000
Arg [5] : _bountyHackerHATVested (uint16): 0
Arg [6] : _tokenLockFactory (address): 0xe198CBb727758b9Ad38a12E1ad475a843e5e730F

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 000000000000000000000000028a7c6873dfa8357c9dcf9c9d76ef2abb66256e
Arg [1] : 00000000000000000000000007368f6a959ef3096230a258dd0af692699c3a4c
Arg [2] : 000000000000000000000000cbe0b90bfe99f827b8bcb5c5ac4b17107caea814
Arg [3] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [4] : 00000000000000000000000000000000000000000000000000000000000003e8
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 000000000000000000000000e198cbb727758b9ad38a12e1ad475a843e5e730f


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.