ETH Price: $1,758.05 (+11.29%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw186714912023-11-28 17:04:47511 days ago1701191087IN
0x00000008...1bE195Be3
0 ETH0.0050074456.49825546
Withdraw186710522023-11-28 15:36:11511 days ago1701185771IN
0x00000008...1bE195Be3
0 ETH0.0044874442.44251367
Progress Epoch186635722023-11-27 14:27:47512 days ago1701095267IN
0x00000008...1bE195Be3
0 ETH0.0083848541.73374295
Unlock186207602023-11-21 14:34:11518 days ago1700577251IN
0x00000008...1bE195Be3
0 ETH0.0038656846.57895832
Unlock185858262023-11-16 17:05:11523 days ago1700154311IN
0x00000008...1bE195Be3
0 ETH0.0066989666.93615087
Withdraw182992182023-10-07 14:31:11563 days ago1696689071IN
0x00000008...1bE195Be3
0 ETH0.000765128.63285269
Progress Epoch182908462023-10-06 10:24:47564 days ago1696587887IN
0x00000008...1bE195Be3
0 ETH0.0020499610.09715494
Unlock182887412023-10-06 3:20:47564 days ago1696562447IN
0x00000008...1bE195Be3
0 ETH0.000617346.16777154
Withdraw182887342023-10-06 3:19:23564 days ago1696562363IN
0x00000008...1bE195Be3
0 ETH0.000534686.03282327
Withdraw182857412023-10-05 17:15:59565 days ago1696526159IN
0x00000008...1bE195Be3
0 ETH0.0012599814.21620792
Withdraw182750992023-10-04 5:34:23566 days ago1696397663IN
0x00000008...1bE195Be3
0 ETH0.000635447.16958354
Progress Epoch182640802023-10-02 16:36:11568 days ago1696264571IN
0x00000008...1bE195Be3
0 ETH0.0054385927.0693956
Withdraw182379892023-09-29 1:05:23571 days ago1695949523IN
0x00000008...1bE195Be3
0 ETH0.000745528.41167598
Unlock182372332023-09-28 22:32:47572 days ago1695940367IN
0x00000008...1bE195Be3
0 ETH0.000900939
Withdraw182372282023-09-28 22:31:47572 days ago1695940307IN
0x00000008...1bE195Be3
0 ETH0.000844999.53392755
Withdraw182350582023-09-28 15:14:59572 days ago1695914099IN
0x00000008...1bE195Be3
0 ETH0.0017685519.95436608
Progress Epoch182282452023-09-27 16:20:11573 days ago1695831611IN
0x00000008...1bE195Be3
0 ETH0.0032167715.84428812
Unlock182241442023-09-27 2:33:11573 days ago1695781991IN
0x00000008...1bE195Be3
0 ETH0.000648277.81131065
Withdraw182241332023-09-27 2:30:59573 days ago1695781859IN
0x00000008...1bE195Be3
0 ETH0.000726818.20056619
Unlock182201202023-09-26 13:02:23574 days ago1695733343IN
0x00000008...1bE195Be3
0 ETH0.0013128415.81667226
Withdraw182201162023-09-26 13:01:35574 days ago1695733295IN
0x00000008...1bE195Be3
0 ETH0.0017089816.1636965
Unlock182200962023-09-26 12:57:35574 days ago1695733055IN
0x00000008...1bE195Be3
0 ETH0.0020704720.68321737
Withdraw182200922023-09-26 12:56:47574 days ago1695733007IN
0x00000008...1bE195Be3
0 ETH0.0019031418
Withdraw181939952023-09-22 21:16:35578 days ago1695417395IN
0x00000008...1bE195Be3
0 ETH0.000747088.42930907
Withdraw181925482023-09-22 16:24:23578 days ago1695399863IN
0x00000008...1bE195Be3
0 ETH0.0012322813.90369722
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x00000089...c16Ccd583
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
RPVault

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 7 : RPVault.sol
//.██████..███████.███████.██.....██████..██████...██████.
//.██...██.██......██......██.....██...██.██...██.██....██
//.██████..█████...█████...██.....██████..██████..██....██
//.██...██.██......██......██.....██......██...██.██....██
//.██...██.███████.██......██.....██......██...██..██████.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { ERC20 } from "solmate/tokens/ERC20.sol";
import { Auth, Authority } from "solmate/auth/Auth.sol";
import { ERC4626Accounting } from "./ERC4626Accounting.sol";
import { IVaultConfig } from "./interfaces/IVaultConfig.sol";

/// @title RPVault
/// @notice epoch-based fund management contract that uses ERC4626 accounting logic.
/// @dev in this version, the contract does not actually use ERC4626 base functions.
/// @dev all vault tokens are stored in-contract, owned by farmer address.
/// @dev all assets are sent to farmer address each epoch change;
/// @dev except for: stored fee, pending withdrawals, & pending deposits.
/// @author mathdroid (https://github.com/mathdroid)
contract RPVault is ERC4626Accounting, Auth {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /// ██████████████ vault metadata ████████████████████████████████████████

    /// @notice aum = **external** assets under management
    uint256 public aum = 0;
    /// @notice aumCap = maximum aum allowed to be stored in the contract
    uint256 public aumCap = 0;
    /// @notice epoch = period of time where aum is being managed
    uint256 public epoch = 0;
    /// @notice farmer = administrative address, responsible for managing aum
    /// @dev the address where funds will go from/to the contract
    address public immutable farmer;
    /// @notice managementBlocksDuration = number of blocks where farmer can make amendments to the contract
    /// @dev this is to prevent mistakes when progressing epoch
    uint256 public managementBlocksDuration = 6000; // avg block time is 15 seconds, so this is ~24 hours
    /// @notice vault config contract
    address public vaultConfigAddress;

    /// ██████████████ fees ███████████████████████████████████████████████████

    /// @notice isFeeEnabled = flag to enable/disable fees
    bool public isFeeEnabled = false;
    /// @notice feeDistributor = address to receive fees from the contract
    address public feeDistributor;
    /// @notice managementFeeBps = management fee in basis points per second
    /// @dev only charged when delta AUM is positive in an epoch
    /// @dev management fee = (assetsExternalEnd - assetsExternalStart) * managementFeeBps / 1e5
    uint256 public managementFeeBps = 2000;
    /// @notice entry/exit fees are charged when a user enters/exits the contract
    uint256 public entryFeeBps = 100;
    /// @notice entry/exit fees are charged when a user enters/exits the contract
    uint256 public exitFeeBps = 100;
    /// @notice storedFee = the amount of stored fee in the contract
    uint256 public storedFee;
    /// @notice helper for fee calculation
    uint256 private constant BASIS = 10000;

    /// ██████████████ vault state per epoch ██████████████████████████████████
    struct VaultState {
        /// @dev starting AUM this epoch
        uint256 assetsExternalStart;
        /// @dev assets deposited by users during this epoch
        uint256 assetsToDeposit;
        /// @dev shares unlocked during this epoch
        uint256 sharesToRedeem;
        /// @dev the number of external AUM at the end of epoch (after fees)
        uint256 assetsExternalEnd;
        /// @dev management fee captured this epoch. maybe 0 if delta AUM <= 0
        /// @dev managementFee + assetsExternalEnd == aum input by farmer
        uint256 managementFee;
        /// @dev total vault tokens supply
        /// @dev no difference start/end of the epoch
        uint256 totalSupply;
        /// @dev last block number where farmer can edit the aum
        /// @dev only farmer can interact with contract before this blocknumber
        uint256 lastManagementBlock;
    }
    /// @notice vaultState = array of vault states per epoch
    mapping(uint256 => VaultState) public vaultStates;

    /// ██████████████ user balances ██████████████████████████████████████████
    struct VaultUser {
        /// @dev assets currently deposited, not yet included in aum
        /// @dev should be zeroed after epoch change (shares minted)
        uint256 assetsDeposited;
        /// @dev last epoch where user deposited assets
        uint256 epochLastDeposited;
        /// @dev glorified `balanceOf`
        uint256 vaultShares;
        /// @dev shares to be unlocked next epoch
        uint256 sharesToRedeem;
        /// @dev the epoch where user can start withdrawing the unlocked shares
        /// @dev use this epoch's redemption rate (aum/totalSupply) to calculate the amount of assets to be withdrawn
        uint256 epochToRedeem;
    }
    /// @notice vaultUsers = array of user balances per address
    mapping(address => VaultUser) public vaultUsers;

    /// ██████████████ errors █████████████████████████████████████████████████

    /// ░░░░░░░░░░░░░░ internal ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice transaction will result in zero shares given
    error DepositReturnsZeroShares();
    /// @notice transaction will result in zero assets given
    error RedeemReturnsZeroAssets();
    /// ░░░░░░░░░░░░░░ epoch ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice vault is still initializing
    error VaultIsInitializing();
    /// @notice vault has been initialized
    error VaultAlreadyInitialized();

    /// ░░░░░░░░░░░░░░ management phase ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice farmer function called outside management phase
    error OnlyAtManagementPhase();
    /// @notice public function called during management phase
    error OnlyOutsideManagementPhase();

    /// ░░░░░░░░░░░░░░ farmer ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice wrong aum cap value (lower than current aum, etc)
    error AumCapInvalid();
    /// @notice wrong ending aum value (infinite growth)
    error AumInvalid();
    /// @notice farmer asset allowance insufficient
    error FarmerInsufficientAllowance();
    /// @notice farmer asset balance insufficient
    error FarmerInsufficientBalance();

    /// ░░░░░░░░░░░░░░ fee ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice error setting fee config
    error FeeSettingsInvalid();
    /// @notice stored fees = 0;
    error FeeIsZero();

    /// ░░░░░░░░░░░░░░ deposit ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice deposit > aum cap
    error DepositExceedsAumCap();
    /// @notice deposit negated by config contract
    error DepositRequirementsNotMet();
    /// @notice deposit fees larger than sent amount
    error DepositFeeExceedsAssets();
    /// @notice has a pending withdrawal
    error DepositBlockedByWithdrawal();

    /// ░░░░░░░░░░░░░░ unlock ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice has a pending withdrawal already
    error UnlockBlockedByWithdrawal();
    /// @notice invalid amount e.g. 0
    error UnlockSharesAmountInvalid();
    /// @notice
    error UnlockExceedsShareBalance();

    /// ░░░░░░░░░░░░░░ withdraw ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice not the epoch to withdraw
    error WithdrawNotAvailableYet();

    /// ██████████████ events █████████████████████████████████████████████████
    /// @notice user events
    event UserDeposit(address indexed user, uint256 amount);
    event UserUnlock(address indexed user, uint256 amount);
    event UserWithdraw(address indexed user, address withdrawalAddress, uint256 amount);

    /// @notice vault events
    event EpochEnd(uint256 epoch, uint256 endingAssets);
    event EpochUpdated(uint256 epoch, uint256 endingAssets);
    event AumCapUpdated(uint256 aumCap);

    /// @notice fee events
    event StoredFeeSent(uint256 amount);
    event FeeUpdated(bool isFeeEnabled, uint256 entryFee, uint256 exitFee, uint256 managementFee);
    event FeeReceiverUpdated(address feeDistributor);

    /// ██████████████ modifiers ██████████████████████████████████████████████

    /// requiresAuth -> from solmate/Auth

    modifier onlyEpochZero() {
        if (epoch != 0) revert VaultAlreadyInitialized();
        _;
    }

    modifier exceptEpochZero() {
        if (epoch < 1) revert VaultIsInitializing();
        _;
    }

    modifier onlyManagementPhase() {
        if (!isManagementPhase()) {
            revert OnlyAtManagementPhase();
        }
        _;
    }

    modifier exceptManagementPhase() {
        if (isManagementPhase()) {
            revert OnlyOutsideManagementPhase();
        }
        _;
    }

    modifier canDeposit(address _user, uint256 _assets) {
        if (userHasPendingWithdrawal(_user)) {
            revert DepositBlockedByWithdrawal();
        }
        // cap must be higher than current AUM + pending deposit + incoming deposit
        if (_assets + getEffectiveAssets() > aumCap) {
            revert DepositExceedsAumCap();
        }

        if (vaultConfigAddress != address(0) && !IVaultConfig(vaultConfigAddress).canDeposit(_user, _assets)) {
            revert DepositRequirementsNotMet();
        }
        _;
    }

    modifier canUnlock(address _user, uint256 _shares) {
        if (_shares < 1) revert UnlockSharesAmountInvalid();
        if (msg.sender != _user) {
            uint256 allowed = allowance[_user][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[_user][msg.sender] = allowed - _shares;
        }

        if (userHasPendingWithdrawal(_user)) revert UnlockBlockedByWithdrawal();
        _;
    }

    modifier canWithdraw(address _user) {
        if (!userHasPendingWithdrawal(_user)) {
            revert WithdrawNotAvailableYet();
        }
        _;
    }

    modifier updatesPendingDeposit(address _user) {
        updatePendingDepositState(_user);
        _;
    }

    /// ██████████████ ERC4626 ████████████████████████████████████████████████
    /// ░░░░░░░░░░░░░░ constructor ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice create an RPVault
    /// @param _name token name, also used as vault name in the UI, omitting `Token` postfix
    /// @param _symbol token symbol, also used as vault symbol in the UI
    /// @param _farmer farmer address, responsible for managing aum
    /// @param _feeDistributor address to receive fees from the contract
    /// @param _underlying asset to be used as underlying asset for the vault
    /// @param _vaultConfig contract address to be used as vault config contract. if 0x0, default config will be used
    constructor(
        string memory _name,
        string memory _symbol,
        address _farmer,
        address _feeDistributor,
        address _underlying,
        address _vaultConfig
    ) ERC4626Accounting(ERC20(_underlying), _name, _symbol) Auth(_farmer, Authority(address(0))) {
        farmer = _farmer;
        feeDistributor = _feeDistributor;
        vaultConfigAddress = _vaultConfig;
    }

    /// @notice Get the amount of productive underlying tokens
    /// @dev used at self deposits/redeems at epoch change
    /// @return aum, total external productive assets
    function totalAssets() public view override returns (uint256) {
        return aum;
    }

    /// ██████████████ farmer functions ███████████████████████████████████████
    /*
        farmer's actions:
            - [x] starts vault with initial settings
            - [x] progress epoch
            - [x] update aum (management phase only)
            - [x] end management phase (management phase only)
            - [x] update aum cap
            - [x] enable/disable fees
            - [x] update fees
            - [x] update fee distributor
            - [x] update vault config address
    */

    /// @notice starts the vault with a custom initial aum
    /// @dev in most cases, initial aum = 0
    /// @param _initialExternalAsset initial aum, must be held by farmer
    /// @param _aumCap maximum asset that can be stored
    function startVault(uint256 _initialExternalAsset, uint256 _aumCap) external onlyEpochZero requiresAuth {
        if (_aumCap < _initialExternalAsset) {
            revert AumCapInvalid();
        }
        if (_initialExternalAsset != 0) {
            uint256 initialShare = _selfDeposit(_initialExternalAsset);
            vaultUsers[msg.sender].vaultShares = initialShare;
        }
        aumCap = _aumCap;
        epoch = 1;
        vaultStates[epoch].assetsExternalStart = aum;
        vaultStates[epoch].totalSupply = totalSupply;
        vaultStates[epoch].lastManagementBlock = block.number;
        emit EpochEnd(0, aum);
    }

    /// @notice Increment epoch from n to (n + 1)
    /// @dev goes to management phase after this function is called
    /// @param _assetsExternalEndBeforeFees current external asset (manual counting by farmer)
    /// @return newAUM (external asset)
    function progressEpoch(uint256 _assetsExternalEndBeforeFees) public requiresAuth exceptEpochZero returns (uint256) {
        // end epoch n
        (
            bool shouldTransferToFarm,
            uint256 totalAssetsToTransfer,
            bool shouldDepositDelta,
            uint256 deltaAssets,
            uint256 managementFee,
            uint256 assetsExternalEnd
        ) = previewProgress(_assetsExternalEndBeforeFees, epoch);

        storedFee += managementFee;
        vaultStates[epoch].managementFee = managementFee;

        aum = assetsExternalEnd;
        vaultStates[epoch].assetsExternalEnd = assetsExternalEnd;

        emit EpochEnd(epoch, aum);
        epoch++;

        // start epoch n + 1
        // transfer assets
        if (totalAssetsToTransfer > 0) {
            if (shouldTransferToFarm) {
                // if there are assets to be transferred to the farm, do it
                transferAssetToFarmer(totalAssetsToTransfer);
            } else {
                // transfer back to contract
                // msg.sender is farmer address
                transferAssetToContract(totalAssetsToTransfer);
            }
        }

        // self deposit/redeem delta
        if (deltaAssets > 0) {
            if (shouldDepositDelta) {
                // self-deposit, update aum
                _selfDeposit(deltaAssets);
            } else {
                // self-redeem, update aum
                _selfRedeem(convertToShares(deltaAssets));
            }
        }

        // if new aum is higher than the cap, increase the cap
        if (aum > aumCap) {
            aumCap = aum;
            emit AumCapUpdated(aumCap);
        }

        //  update vault state
        vaultStates[epoch].assetsExternalStart = aum;
        vaultStates[epoch].totalSupply = totalSupply;
        vaultStates[epoch].lastManagementBlock = block.number + managementBlocksDuration;
        return aum;
    }

    /// @notice amends last epoch's aum update
    /// @dev callable at management phase only
    /// @param _assetsExternalEndBeforeFees current external asset (manual counting by farmer)
    /// @return newAUM (external asset)
    // solhint-disable-next-line code-complexity
    function editAUM(uint256 _assetsExternalEndBeforeFees)
        public
        onlyManagementPhase
        requiresAuth
        returns (uint256 newAUM)
    {
        uint256 lastEpoch = epoch - 1;
        uint256 lastAssetsExternalEnd = vaultStates[lastEpoch].assetsExternalEnd;
        uint256 lastManagementFee = vaultStates[lastEpoch].managementFee;
        uint256 lastTotalSupply = vaultStates[lastEpoch].totalSupply;

        if (_assetsExternalEndBeforeFees == lastAssetsExternalEnd + lastManagementFee) {
            // no change in aum
            return lastAssetsExternalEnd;
        }
        (
            bool didTransferToFarm,
            uint256 totalAssetsTransferred, // bool didDepositDelta,
            ,
            ,
            ,

        ) = previewProgress(lastAssetsExternalEnd + lastManagementFee, lastEpoch);

        /// @dev rather than saving gas by combining these into 1 transfers but with overflow handling, we do it in 2
        /// @dev gas is paid by farmer (upkeep)

        // revert transfers
        if (totalAssetsTransferred > 0) {
            if (didTransferToFarm) {
                // revert
                transferAssetToContract(totalAssetsTransferred);
            } else {
                transferAssetToFarmer(totalAssetsTransferred);
            }
        }

        // // revert deposit/redeem using latest rate, update aum automatically
        if (totalSupply > lastTotalSupply) {
            _burn(address(this), totalSupply - lastTotalSupply);
        }

        if (totalSupply < lastTotalSupply) {
            _mint(address(this), lastTotalSupply - totalSupply);
        }

        // /// @dev by this point, aum should be the same as last epoch's aum
        storedFee -= lastManagementFee;
        epoch = lastEpoch;
        return progressEpoch(_assetsExternalEndBeforeFees);
    }

    /// @notice ends management phase, allow users to deposit/unlock/withdraw
    function endManagementPhase() public requiresAuth onlyManagementPhase {
        vaultStates[epoch].lastManagementBlock = block.number;
    }

    /// @notice change AUM cap
    /// @param _aumCap new AUM cap
    function updateAumCap(uint256 _aumCap) public requiresAuth {
        if (aumCap < getEffectiveAssets()) {
            revert AumCapInvalid();
        }
        aumCap = _aumCap;
    }

    /// @notice toggle fees on/off
    /// @param _isFeeEnabled true to enable fees, false to disable fees
    function setIsFeeEnabled(bool _isFeeEnabled) public requiresAuth {
        if (isFeeEnabled == _isFeeEnabled) {
            revert FeeSettingsInvalid();
        }
        isFeeEnabled = _isFeeEnabled;
    }

    /// @notice update fees
    function setFees(
        uint256 _managementFeeBps,
        uint256 _entryFeeBps,
        uint256 _exitFeeBps
    ) public requiresAuth {
        if (_managementFeeBps > BASIS || _entryFeeBps > BASIS || _exitFeeBps > BASIS) {
            revert FeeSettingsInvalid();
        }
        managementFeeBps = _managementFeeBps;
        entryFeeBps = _entryFeeBps;
        exitFeeBps = _exitFeeBps;
    }

    /// @notice update fee distributor
    function setFeeDistributor(address _feeDistributor) public requiresAuth {
        if (_feeDistributor == address(0)) {
            revert FeeSettingsInvalid();
        }
        feeDistributor = _feeDistributor;
    }

    function setVaultConfigAddress(address _vaultConfigAddress) public requiresAuth {
        vaultConfigAddress = _vaultConfigAddress;
    }

    /// ██████████████ user functions █████████████████████████████████████████
    /*
        A user can only do:
            - [x] deposit (not to be confused with ERC4626 deposit)
                - minimum/maximum rule set in the vaultConfig contract
            - [x] unlock
            - [x] withdraw (not to be confused with ERC4626 withdraw)

    */

    /// ░░░░░░░░░░░░░░ deposit ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice a user stores assets in the contract to enter in the next epoch
    /// @dev funds should be withdrawable before epoch progresses
    /// @dev actual share minting happens at the epoch progression
    /// @dev share minting uses next epoch's starting exchange rate
    function deposit(uint256 _assets) external exceptEpochZero exceptManagementPhase returns (uint256) {
        return deposit(_assets, msg.sender);
    }

    function deposit(uint256 _assets, address _for)
        public
        exceptEpochZero
        exceptManagementPhase
        canDeposit(_for, _assets)
        updatesPendingDeposit(_for)
        returns (uint256)
    {
        uint256 depositFee = getDepositFee(_assets, _for);
        if (depositFee >= _assets) {
            revert DepositFeeExceedsAssets();
        }
        uint256 netAssets = _assets - depositFee;

        storedFee += depositFee;

        /// last deposit epoch = 0
        /// assetDeposited = 0

        vaultUsers[_for].epochLastDeposited = epoch;
        vaultUsers[_for].assetsDeposited += netAssets;

        // update vault state
        vaultStates[epoch].assetsToDeposit += netAssets;

        // transfer asset to vault
        asset.safeTransferFrom(msg.sender, address(this), _assets);
        emit UserDeposit(_for, netAssets);
        return netAssets;
    }

    /// ░░░░░░░░░░░░░░ unlock (withdraw 1/2) ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice unlock _shares for withdrawal at next available epoch
    function unlock(uint256 _shares) external exceptEpochZero exceptManagementPhase returns (uint256) {
        return unlock(_shares, msg.sender);
    }

    function unlock(uint256 _shares, address _owner)
        public
        exceptEpochZero
        exceptManagementPhase
        canUnlock(_owner, _shares)
        updatesPendingDeposit(_owner)
        returns (uint256)
    {
        // updatePendingDepositState(msg.sender);

        if (vaultUsers[_owner].vaultShares < vaultUsers[_owner].sharesToRedeem + _shares) {
            revert UnlockExceedsShareBalance();
        }

        vaultUsers[_owner].sharesToRedeem += _shares;
        vaultUsers[_owner].epochToRedeem = epoch + 1;
        vaultStates[epoch].sharesToRedeem += _shares;

        emit UserUnlock(_owner, _shares);
        return _shares;
    }

    /// ░░░░░░░░░░░░░░ finalize (withdraw 2/2) ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    /// @notice withdraw all available asset for user
    function withdraw() external exceptEpochZero exceptManagementPhase returns (uint256) {
        return withdraw(msg.sender);
    }

    function withdraw(address _to)
        public
        exceptEpochZero
        exceptManagementPhase
        canWithdraw(msg.sender)
        updatesPendingDeposit(msg.sender)
        returns (uint256)
    {
        (uint256 totalAssetValue, uint256 withdrawalFee) = getWithdrawalAmount(msg.sender);

        vaultUsers[msg.sender].vaultShares -= vaultUsers[msg.sender].sharesToRedeem;
        vaultUsers[msg.sender].sharesToRedeem = 0;
        vaultUsers[msg.sender].epochToRedeem = 0;
        storedFee += withdrawalFee;

        if (withdrawalFee == totalAssetValue) {
            // @dev for really small values, we can't afford to lose precision
            return 0;
        }

        uint256 transferValue = totalAssetValue - withdrawalFee;
        asset.transfer(_to, transferValue);

        emit UserWithdraw(msg.sender, _to, transferValue);
        return transferValue;
    }

    /// ██████████████ public functions ███████████████████████████████████████
    /*
        public functions:
            - [x] preview funds flow from/to contract next epoch
            - [x] check if user can deposit/unlock/withdraw
            - [x] send stored fees to fee distributor
            - [x] get maximum deposit amount
    */
    /// @notice preview funds flow from/to contract next epoch
    /// @dev assets to transfer = deltaAssets - managementFee
    /// @dev sign shows direction of transfer (true = to farm, false = to contract)
    /// @param _assetsExternalEndBeforeFees amount of external aum before fees
    /// @return shouldTransferToFarm direction of funds to transfer
    /// @return totalAssetsToTransfer amount of assets to transfer
    /// @return shouldDepositDelta true if deltaAssets should be deposited, false if deltaAssets should be redeemed
    /// @return deltaAssets amount of assets to deposit/redeem
    /// @return managementFee amount of management fee for next epoch
    /// @return assetsExternalEnd amount of vault ending aum. assetsExternalEnd = _assetsExternalEndBeforeFees - fees
    function previewProgress(uint256 _assetsExternalEndBeforeFees)
        public
        view
        returns (
            bool shouldTransferToFarm,
            uint256 totalAssetsToTransfer,
            bool shouldDepositDelta,
            uint256 deltaAssets,
            uint256 managementFee,
            uint256 assetsExternalEnd
        )
    {
        return previewProgress(_assetsExternalEndBeforeFees, epoch);
    }

    function previewProgress(uint256 _assetsExternalEndBeforeFees, uint256 _epoch)
        public
        view
        returns (
            bool shouldTransferToFarm,
            uint256 totalAssetsToTransfer,
            bool shouldDepositDelta,
            uint256 deltaAssets,
            uint256 managementFee,
            uint256 assetsExternalEnd
        )
    {
        uint256 epochTotalSupply = _epoch == epoch ? totalSupply : vaultStates[_epoch].totalSupply;

        uint256 assetsExternalStart = vaultStates[_epoch].assetsExternalStart;
        uint256 assetsToDeposit = vaultStates[_epoch].assetsToDeposit;
        uint256 sharesToRedeem = vaultStates[_epoch].sharesToRedeem;

        if (assetsExternalStart == 0 && _assetsExternalEndBeforeFees > 0) {
            revert AumInvalid();
        }

        managementFee = getManagementFee(assetsExternalStart, _assetsExternalEndBeforeFees);
        assetsExternalEnd = _assetsExternalEndBeforeFees - managementFee;

        /// @dev at 0 supply, rate is 1:1
        uint256 redeemAssetValue = epochTotalSupply == 0
            ? sharesToRedeem
            : sharesToRedeem.mulDivDown(assetsExternalEnd, epochTotalSupply);

        /// @dev if true, the delta (deltaAssets) will be used in selfDeposit.
        /// @dev if false, the delta will be "soft-used" in selfRedeem(shares);

        shouldDepositDelta = assetsToDeposit > redeemAssetValue;
        deltaAssets = shouldDepositDelta ? assetsToDeposit - redeemAssetValue : redeemAssetValue - assetsToDeposit;

        if (shouldDepositDelta) {
            // if deposit is bigger, transfer to farm
            // subtract by management fee
            if (managementFee > deltaAssets) {
                // reverse if fee > delta
                totalAssetsToTransfer = managementFee - deltaAssets;
                shouldTransferToFarm = false;
            } else {
                totalAssetsToTransfer = deltaAssets - managementFee;
                shouldTransferToFarm = true;
            }
        } else {
            // if redeem value is bigger, transfer to contract
            // add management fee
            totalAssetsToTransfer = deltaAssets + managementFee;
            shouldTransferToFarm = false;
        }

        return (
            shouldTransferToFarm,
            totalAssetsToTransfer,
            shouldDepositDelta,
            deltaAssets,
            managementFee,
            assetsExternalEnd
        );
    }

    function isManagementPhase() public view returns (bool) {
        return block.number <= vaultStates[epoch].lastManagementBlock;
    }

    /// @notice sends stored fee to fee distributor
    function sendFee() public exceptManagementPhase {
        if (storedFee == 0) {
            revert FeeIsZero();
        }
        uint256 amount = storedFee;
        storedFee = 0;
        asset.transfer(feeDistributor, amount);
        emit StoredFeeSent(storedFee);
    }

    /// @notice get maximum deposit amount
    function getMaxDeposit() public view returns (uint256) {
        return aumCap - getEffectiveAssets();
    }

    /// @notice preview deposit on epoch
    function previewDepositEpoch(uint256 _assets, uint256 _epoch) public view returns (uint256) {
        if (vaultStates[_epoch].totalSupply == 0 || vaultStates[_epoch].assetsExternalStart == 0) {
            return _assets;
        }
        return _assets.mulDivDown(vaultStates[_epoch].totalSupply, vaultStates[_epoch].assetsExternalStart);
    }

    /// ██████████████ internals ██████████████████████████████████████████████
    // TODO: internalize before deploy
    /// @notice self-deposit, uses ERC4626 calculations, without actual transfer
    /// @dev updates AUM
    /// @param _assets number of assets to deposit
    /// @return shares minted
    function _selfDeposit(uint256 _assets) internal returns (uint256) {
        uint256 shares;
        if ((shares = previewDeposit(_assets)) == 0) revert DepositReturnsZeroShares();

        _mint(address(this), shares);
        aum += _assets;

        emit Deposit(msg.sender, address(this), _assets, shares);

        return shares;
    }

    /// @notice self-redeem, uses ERC4626 calculations, without actual transfer
    /// @dev updates AUM
    /// @param _shares number of shares to redeem
    /// @return assets value of burned shares
    function _selfRedeem(uint256 _shares) internal returns (uint256) {
        uint256 assets;
        // Check for rounding error since we round down in previewRedeem.
        if ((assets = previewRedeem(_shares)) == 0) revert RedeemReturnsZeroAssets();

        _burn(address(this), _shares);
        aum -= assets;

        emit Withdraw(msg.sender, address(this), address(this), assets, _shares);
        return assets;
    }

    /// @notice calculate management fee based on aum change
    /// @param _assetsExternalStart assets at start of epoch
    /// @param _assetsExternalEndBeforeFees assets at end of epoch
    /// @return managementFee management fees in asset
    function getManagementFee(uint256 _assetsExternalStart, uint256 _assetsExternalEndBeforeFees)
        internal
        view
        returns (uint256)
    {
        if (!isFeeEnabled) {
            return 0;
        }
        return
            (_assetsExternalEndBeforeFees > _assetsExternalStart && managementFeeBps > 0)
                ? managementFeeBps.mulDivUp(_assetsExternalEndBeforeFees - _assetsExternalStart, BASIS)
                : 0;
    }

    function transferAssetToFarmer(uint256 _assets) internal returns (bool) {
        return asset.transfer(farmer, _assets);
    }

    function transferAssetToContract(uint256 _assets) internal {
        if (asset.allowance(msg.sender, address(this)) < _assets) {
            revert FarmerInsufficientAllowance();
        }
        if (asset.balanceOf(msg.sender) < _assets) {
            revert FarmerInsufficientBalance();
        }
        return asset.safeTransferFrom(msg.sender, address(this), _assets);
    }

    function getEffectiveAssets() internal view returns (uint256) {
        return aum + vaultStates[epoch].assetsToDeposit;
    }

    /// @notice update VaultUser's data if they have pending deposits
    /// @param _user address of the VaultUser
    /// @dev after this, last deposit epoch = 0, assetDeposited = 0
    /// @dev can be manually called
    function updatePendingDepositState(address _user) public {
        // @dev check if user has already stored assets
        if (userHasPendingDeposit(_user)) {
            // @dev user should already have shares here, let's increment
            vaultUsers[_user].vaultShares += previewDepositEpoch(
                vaultUsers[_user].assetsDeposited,
                vaultUsers[_user].epochLastDeposited + 1
            );

            vaultUsers[_user].assetsDeposited = 0;
            vaultUsers[_user].epochLastDeposited = 0;
        }
    }

    /// @notice check if user has pending deposits
    /// @param _user address of the VaultUser
    /// @return true if user has pending deposits
    function userHasPendingDeposit(address _user) public view returns (bool) {
        uint256 userEpoch = vaultUsers[_user].epochLastDeposited;
        return userEpoch != 0 && epoch > userEpoch;
    }

    /// @notice get deposit fee for user
    function getDepositFee(uint256 _assets, address _user) public view returns (uint256) {
        if (vaultConfigAddress != address(0) && IVaultConfig(vaultConfigAddress).isFeeEnabled(_user)) {
            return _assets.mulDivUp(IVaultConfig(vaultConfigAddress).entryFeeBps(_user), BASIS);
        } else {
            return isFeeEnabled ? _assets.mulDivUp(entryFeeBps, BASIS) : 0;
        }
    }

    /// @notice check if a user has pending, unlocked funds to withdraw
    function userHasPendingWithdrawal(address _user) public view returns (bool) {
        return vaultUsers[_user].epochToRedeem > 0 && vaultUsers[_user].epochToRedeem <= epoch;
    }

    function getStoredValue(address _user) public view returns (uint256 userAssetValue) {
        VaultUser memory user = vaultUsers[_user];

        uint256 userShares = user.vaultShares;

        if (userHasPendingDeposit(_user)) {
            // shares has been minted, user state not yet updated
            userShares += previewDepositEpoch(user.assetsDeposited, user.epochLastDeposited + 1);
        } else {
            // still currently pending (no minted shares yet)
            userAssetValue += user.assetsDeposited;
        }

        userAssetValue += convertToAssets(userShares);
    }

    function getWithdrawalFee(uint256 _assets, address _user) public view returns (uint256) {
        if (vaultConfigAddress != address(0) && IVaultConfig(vaultConfigAddress).isFeeEnabled(_user)) {
            return _assets.mulDivUp(IVaultConfig(vaultConfigAddress).exitFeeBps(_user), BASIS);
        } else {
            return isFeeEnabled ? _assets.mulDivUp(exitFeeBps, BASIS) : 0;
        }
    }

    function getWithdrawalAmount(address _owner) public view returns (uint256, uint256) {
        if (!userHasPendingWithdrawal(_owner)) return (0, 0);
        uint256 epochToRedeem = vaultUsers[_owner].epochToRedeem;
        uint256 sharesToRedeem = vaultUsers[_owner].sharesToRedeem;
        uint256 assetsExternalStart = vaultStates[epochToRedeem].assetsExternalStart;
        uint256 totalSupplyAtRedeem = vaultStates[epochToRedeem].totalSupply;

        if (assetsExternalStart == 0 || totalSupplyAtRedeem == 0) {
            return (0, 0);
        }

        uint256 totalAssetValue = sharesToRedeem.mulDivDown(
            vaultStates[epochToRedeem].assetsExternalStart,
            vaultStates[epochToRedeem].totalSupply
        );
        uint256 fee = getWithdrawalFee(totalAssetValue, _owner);

        return (totalAssetValue, fee);
    }
}

File 2 of 7 : FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // Divide z by the denominator.
            z := div(z, denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // First, divide z - 1 by the denominator and add 1.
            // We allow z - 1 to underflow if z is 0, because we multiply the
            // end result by 0 if z is zero, ensuring we return 0 if z is zero.
            z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        assembly {
            // Start off with z at 1.
            z := 1

            // Used below to help find a nearby power of 2.
            let y := x

            // Find the lowest power of 2 that is at least sqrt(x).
            if iszero(lt(y, 0x100000000000000000000000000000000)) {
                y := shr(128, y) // Like dividing by 2 ** 128.
                z := shl(64, z) // Like multiplying by 2 ** 64.
            }
            if iszero(lt(y, 0x10000000000000000)) {
                y := shr(64, y) // Like dividing by 2 ** 64.
                z := shl(32, z) // Like multiplying by 2 ** 32.
            }
            if iszero(lt(y, 0x100000000)) {
                y := shr(32, y) // Like dividing by 2 ** 32.
                z := shl(16, z) // Like multiplying by 2 ** 16.
            }
            if iszero(lt(y, 0x10000)) {
                y := shr(16, y) // Like dividing by 2 ** 16.
                z := shl(8, z) // Like multiplying by 2 ** 8.
            }
            if iszero(lt(y, 0x100)) {
                y := shr(8, y) // Like dividing by 2 ** 8.
                z := shl(4, z) // Like multiplying by 2 ** 4.
            }
            if iszero(lt(y, 0x10)) {
                y := shr(4, y) // Like dividing by 2 ** 4.
                z := shl(2, z) // Like multiplying by 2 ** 2.
            }
            if iszero(lt(y, 0x8)) {
                // Equivalent to 2 ** z.
                z := shl(1, z)
            }

            // Shifting right by 1 is like dividing by 2.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // Compute a rounded down version of z.
            let zRoundDown := div(x, z)

            // If zRoundDown is smaller, use it.
            if lt(zRoundDown, z) {
                z := zRoundDown
            }
        }
    }
}

File 3 of 7 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 4 of 7 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 5 of 7 : Auth.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
    event OwnerUpdated(address indexed user, address indexed newOwner);

    event AuthorityUpdated(address indexed user, Authority indexed newAuthority);

    address public owner;

    Authority public authority;

    constructor(address _owner, Authority _authority) {
        owner = _owner;
        authority = _authority;

        emit OwnerUpdated(msg.sender, _owner);
        emit AuthorityUpdated(msg.sender, _authority);
    }

    modifier requiresAuth() virtual {
        require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");

        _;
    }

    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.

        // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
        // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
        return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
    }

    function setAuthority(Authority newAuthority) public virtual {
        // We check if the caller is the owner first because we want to ensure they can
        // always swap out the authority even if it's reverting or using up a lot of gas.
        require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));

        authority = newAuthority;

        emit AuthorityUpdated(msg.sender, newAuthority);
    }

    function setOwner(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnerUpdated(msg.sender, newOwner);
    }
}

/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
    function canCall(
        address user,
        address target,
        bytes4 functionSig
    ) external view returns (bool);
}

File 6 of 7 : ERC4626Accounting.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.4 <0.9.0;

import { ERC20 } from "solmate/tokens/ERC20.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol";

/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @dev derived from Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol)
abstract contract ERC4626Accounting is ERC20 {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

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

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    ERC20 public immutable asset;

    constructor(
        ERC20 _asset,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol, _asset.decimals()) {
        asset = _asset;
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    /* None! Ta da! */

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

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

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
    }

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

    function previewMint(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

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

File 7 of 7 : IVaultConfig.sol
//.██████..███████.███████.██.....██████..██████...██████.
//.██...██.██......██......██.....██...██.██...██.██....██
//.██████..█████...█████...██.....██████..██████..██....██
//.██...██.██......██......██.....██......██...██.██....██
//.██...██.███████.██......██.....██......██...██..██████.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

interface IVaultConfig {
    function canDeposit(address _user, uint256 _assets) external view returns (bool);

    function isFeeEnabled(address _user) external view returns (bool);

    function entryFeeBps(address _user) external view returns (uint256);

    function exitFeeBps(address _user) external view returns (uint256);

    /// @dev management fee is the same for everyone
    // function managementFeeBps() external view returns (uint256);
}

Settings
{
  "remappings": [
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solmate/=lib/solmate/src/",
    "src/=src/",
    "test/=test/",
    "script/=script/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_farmer","type":"address"},{"internalType":"address","name":"_feeDistributor","type":"address"},{"internalType":"address","name":"_underlying","type":"address"},{"internalType":"address","name":"_vaultConfig","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AumCapInvalid","type":"error"},{"inputs":[],"name":"AumInvalid","type":"error"},{"inputs":[],"name":"DepositBlockedByWithdrawal","type":"error"},{"inputs":[],"name":"DepositExceedsAumCap","type":"error"},{"inputs":[],"name":"DepositFeeExceedsAssets","type":"error"},{"inputs":[],"name":"DepositRequirementsNotMet","type":"error"},{"inputs":[],"name":"DepositReturnsZeroShares","type":"error"},{"inputs":[],"name":"FarmerInsufficientAllowance","type":"error"},{"inputs":[],"name":"FarmerInsufficientBalance","type":"error"},{"inputs":[],"name":"FeeIsZero","type":"error"},{"inputs":[],"name":"FeeSettingsInvalid","type":"error"},{"inputs":[],"name":"OnlyAtManagementPhase","type":"error"},{"inputs":[],"name":"OnlyOutsideManagementPhase","type":"error"},{"inputs":[],"name":"RedeemReturnsZeroAssets","type":"error"},{"inputs":[],"name":"UnlockBlockedByWithdrawal","type":"error"},{"inputs":[],"name":"UnlockExceedsShareBalance","type":"error"},{"inputs":[],"name":"UnlockSharesAmountInvalid","type":"error"},{"inputs":[],"name":"VaultAlreadyInitialized","type":"error"},{"inputs":[],"name":"VaultIsInitializing","type":"error"},{"inputs":[],"name":"WithdrawNotAvailableYet","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"aumCap","type":"uint256"}],"name":"AumCapUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endingAssets","type":"uint256"}],"name":"EpochEnd","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endingAssets","type":"uint256"}],"name":"EpochUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeDistributor","type":"address"}],"name":"FeeReceiverUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isFeeEnabled","type":"bool"},{"indexed":false,"internalType":"uint256","name":"entryFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"exitFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"managementFee","type":"uint256"}],"name":"FeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"StoredFeeSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UserDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UserUnlock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"withdrawalAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UserWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"aum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"aumCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_for","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetsExternalEndBeforeFees","type":"uint256"}],"name":"editAUM","outputs":[{"internalType":"uint256","name":"newAUM","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endManagementPhase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"entryFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exitFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"farmer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeDistributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getDepositFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getStoredValue","outputs":[{"internalType":"uint256","name":"userAssetValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"getWithdrawalAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getWithdrawalFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFeeEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isManagementPhase","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"managementBlocksDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"managementFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"previewDepositEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetsExternalEndBeforeFees","type":"uint256"}],"name":"previewProgress","outputs":[{"internalType":"bool","name":"shouldTransferToFarm","type":"bool"},{"internalType":"uint256","name":"totalAssetsToTransfer","type":"uint256"},{"internalType":"bool","name":"shouldDepositDelta","type":"bool"},{"internalType":"uint256","name":"deltaAssets","type":"uint256"},{"internalType":"uint256","name":"managementFee","type":"uint256"},{"internalType":"uint256","name":"assetsExternalEnd","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetsExternalEndBeforeFees","type":"uint256"},{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"previewProgress","outputs":[{"internalType":"bool","name":"shouldTransferToFarm","type":"bool"},{"internalType":"uint256","name":"totalAssetsToTransfer","type":"uint256"},{"internalType":"bool","name":"shouldDepositDelta","type":"bool"},{"internalType":"uint256","name":"deltaAssets","type":"uint256"},{"internalType":"uint256","name":"managementFee","type":"uint256"},{"internalType":"uint256","name":"assetsExternalEnd","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetsExternalEndBeforeFees","type":"uint256"}],"name":"progressEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sendFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeDistributor","type":"address"}],"name":"setFeeDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_managementFeeBps","type":"uint256"},{"internalType":"uint256","name":"_entryFeeBps","type":"uint256"},{"internalType":"uint256","name":"_exitFeeBps","type":"uint256"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isFeeEnabled","type":"bool"}],"name":"setIsFeeEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vaultConfigAddress","type":"address"}],"name":"setVaultConfigAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_initialExternalAsset","type":"uint256"},{"internalType":"uint256","name":"_aumCap","type":"uint256"}],"name":"startVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storedFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"unlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_owner","type":"address"}],"name":"unlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_aumCap","type":"uint256"}],"name":"updateAumCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"updatePendingDepositState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"userHasPendingDeposit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"userHasPendingWithdrawal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultConfigAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vaultStates","outputs":[{"internalType":"uint256","name":"assetsExternalStart","type":"uint256"},{"internalType":"uint256","name":"assetsToDeposit","type":"uint256"},{"internalType":"uint256","name":"sharesToRedeem","type":"uint256"},{"internalType":"uint256","name":"assetsExternalEnd","type":"uint256"},{"internalType":"uint256","name":"managementFee","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"lastManagementBlock","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"vaultUsers","outputs":[{"internalType":"uint256","name":"assetsDeposited","type":"uint256"},{"internalType":"uint256","name":"epochLastDeposited","type":"uint256"},{"internalType":"uint256","name":"vaultShares","type":"uint256"},{"internalType":"uint256","name":"sharesToRedeem","type":"uint256"},{"internalType":"uint256","name":"epochToRedeem","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103e65760003560e01c80637a9e5e4b1161020a578063c1dbbc3111610125578063d811fcf0116100b8578063ef8b30f711610087578063ef8b30f71461099a578063f1d2ec1d146109ad578063f366d950146109c0578063f70881a3146109d3578063fa37a9ba146109e657600080fd5b8063d811fcf014610922578063dc4a5bff14610949578063dd62ed3e1461095c578063ecda74cf1461098757600080fd5b8063cec10c11116100f4578063cec10c11146108e0578063d49e271d146108f3578063d4d4be2f146108fc578063d505accf1461090f57600080fd5b8063c1dbbc3114610894578063c6e6f592146108a7578063c7ba9c72146108ba578063ccfc2e8d146108cd57600080fd5b806399f2dda61161019d578063ae39279f1161016c578063ae39279f14610853578063b3d7f6b91461085b578063b6b55f251461086e578063bf7e214f1461088157600080fd5b806399f2dda61461081c578063a4dbc6a814610825578063a5cc958d1461082d578063a9059cbb1461084057600080fd5b80638da5cb5b116101d95780638da5cb5b146107e5578063900cf0cf146107f8578063907e98cd1461080157806395d89b411461081457600080fd5b80637a9e5e4b146107305780637ecebe0014610743578063808dd5eb14610763578063849efb711461078057600080fd5b8063344de0ac11610305578063578334ee1161029857806361bc2e331161026757806361bc2e33146106ce5780636e553f65146106e15780636ea97367146106f457806370a082311461070757806371634e321461072757600080fd5b8063578334ee1461069657806357b17a521461069f5780635a157a25146106a85780636198e339146106bb57600080fd5b80633ccfd60b116102d45780633ccfd60b146106405780634cdad5061461064857806351cff8d91461065b57806356582bf91461066e57600080fd5b8063344de0ac146105f55780633644e515146106085780633813c35a1461061057806338d52e0f1461061957600080fd5b80630d43e8ad1161037d57806318160ddd1161034c57806318160ddd146105585780631c4ad9151461056157806323b872dd146105a9578063313ce567146105bc57600080fd5b80630d43e8ad146104865780630e66dc69146104b1578063119435a4146104c557806313af40351461054557600080fd5b8063095ea7b3116103b9578063095ea7b31461043d5780630a28a477146104605780630ab51bac146104735780630b38bed91461047c57600080fd5b806301e1d114146103eb57806306fdde031461040257806307a2d13a146104175780630807ac571461042a575b600080fd5b6008545b6040519081526020015b60405180910390f35b61040a6109f9565b6040516103f99190612ed8565b6103ef610425366004612f2d565b610a87565b6103ef610438366004612f46565b610ab5565b61045061044b366004612f7d565b610b14565b60405190151581526020016103f9565b6103ef61046e366004612f2d565b610b80565b6103ef60085481565b610484610ba1565b005b600d54610499906001600160a01b031681565b6040516001600160a01b0390911681526020016103f9565b600c5461045090600160a01b900460ff1681565b6105106104d3366004612f2d565b6012602052600090815260409020805460018201546002830154600384015460048501546005860154600690960154949593949293919290919087565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016103f9565b610484610553366004612fa9565b610c2b565b6103ef60025481565b61057461056f366004612f2d565b610ca9565b6040805196151587526020870195909552921515938501939093526060840152608083019190915260a082015260c0016103f9565b6104506105b7366004612fc6565b610cd2565b6105e37f000000000000000000000000000000000000000000000000000000000000000681565b60405160ff90911681526020016103f9565b610484610603366004612fa9565b610db2565b6103ef610e06565b6103ef600e5481565b6104997f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b6103ef610e61565b6103ef610656366004612f2d565b610ec4565b6103ef610669366004612fa9565b610ecf565b61068161067c366004612fa9565b6110d2565b604080519283526020830191909152016103f9565b6103ef600f5481565b6103ef60105481565b6103ef6106b6366004612f2d565b611183565b6103ef6106c9366004612f2d565b6112f0565b600c54610499906001600160a01b031681565b6103ef6106ef366004613007565b611354565b610484610702366004612fa9565b6115f8565b6103ef610715366004612fa9565b60036020526000908152604090205481565b6103ef60115481565b61048461073e366004612fa9565b611687565b6103ef610751366004612fa9565b60056020526000908152604090205481565b600a54600090815260126020526040902060060154431115610450565b6107bd61078e366004612fa9565b601360205260009081526040902080546001820154600283015460038401546004909401549293919290919085565b604080519586526020860194909452928401919091526060830152608082015260a0016103f9565b600654610499906001600160a01b031681565b6103ef600a5481565b6103ef61080f366004612fa9565b611771565b61040a611822565b6103ef60095481565b6103ef61182f565b61048461083b366004612f46565b611846565b61045061084e366004612f7d565b6119a8565b610484611a0e565b6103ef610869366004612f2d565b611b45565b6103ef61087c366004612f2d565b611b65565b600754610499906001600160a01b031681565b6103ef6108a2366004613007565b611bc9565b6103ef6108b5366004612f2d565b611cff565b6103ef6108c8366004612f2d565b611d20565b6104846108db366004612fa9565b611f1f565b6104846108ee366004613037565b611f9a565b6103ef600b5481565b61045061090a366004612fa9565b612015565b61048461091d366004613063565b612061565b6104997f0000000000000000000000004457df4a5bccf796662b6374d5947c881cc83ac781565b6104846109573660046130e8565b6122a5565b6103ef61096a366004613105565b600460209081526000928352604080842090915290825290205481565b610574610995366004612f46565b612328565b6103ef6109a8366004612f2d565b612453565b6103ef6109bb366004613007565b61245e565b6104846109ce366004612f2d565b6126a9565b6104506109e1366004612fa9565b61270a565b6103ef6109f4366004613007565b61273a565b60008054610a0690613133565b80601f0160208091040260200160405190810160405280929190818152602001828054610a3290613133565b8015610a7f5780601f10610a5457610100808354040283529160200191610a7f565b820191906000526020600020905b815481529060010190602001808311610a6257829003601f168201915b505050505081565b6002546000908015610aac57610aa7610a9f60085490565b849083612822565b610aae565b825b9392505050565b6000818152601260205260408120600501541580610adf5750600082815260126020526040902054155b15610aeb575081610b0e565b600082815260126020526040902060058101549054610b0b918591612822565b90505b92915050565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610b6f9086815260200190565b60405180910390a350600192915050565b6002546000908015610aac57610aa781610b9960085490565b859190612841565b610bb7336000356001600160e01b03191661286f565b610bdc5760405162461bcd60e51b8152600401610bd39061316d565b60405180910390fd5b600a54600090815260126020526040902060060154431115610c11576040516354a9d44560e01b815260040160405180910390fd5b600a54600090815260126020526040902043600690910155565b610c41336000356001600160e01b03191661286f565b610c5d5760405162461bcd60e51b8152600401610bd39061316d565b600680546001600160a01b0319166001600160a01b03831690811790915560405133907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7690600090a350565b600080600080600080610cbe87600a54612328565b949c939b5091995097509550909350915050565b6001600160a01b03831660009081526004602090815260408083203384529091528120546000198114610d2e57610d0983826131a9565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b03851660009081526003602052604081208054859290610d569084906131a9565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716906000805160206132f483398151915290610d9f9087815260200190565b60405180910390a3506001949350505050565b610dc8336000356001600160e01b03191661286f565b610de45760405162461bcd60e51b8152600401610bd39061316d565b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b60007f00000000000000000000000000000000000000000000000000000000000000014614610e3c57610e37612918565b905090565b507fb870d8574d8f3f1ba4f7675cd64f74f3c42de31dc6f1f51d07ae153028ca759b90565b60006001600a541015610e87576040516324875a2960e11b815260040160405180910390fd5b600a546000908152601260205260409020600601544311610ebb576040516335e7b1ad60e01b815260040160405180910390fd5b610e3733610ecf565b6000610b0e82610a87565b60006001600a541015610ef5576040516324875a2960e11b815260040160405180910390fd5b600a546000908152601260205260409020600601544311610f29576040516335e7b1ad60e01b815260040160405180910390fd5b33610f3381612015565b610f50576040516377a59ab560e11b815260040160405180910390fd5b33610f5a816115f8565b600080610f66336110d2565b33600090815260136020526040812060038101546002909101805494965092945092610f939084906131a9565b90915550503360009081526013602052604081206003810182905560040181905560118054839290610fc69084906131c0565b9091555050818103610fdd576000945050506110cb565b6000610fe982846131a9565b60405163a9059cbb60e01b81526001600160a01b038981166004830152602482018390529192507f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489091169063a9059cbb906044016020604051808303816000875af115801561105d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108191906131d8565b50604080516001600160a01b03891681526020810183905233917f6985a6dd52aeb8194df40b7af2f362f362440affc39c1314649abc28dbf6b628910160405180910390a2945050505b5050919050565b6000806110de83612015565b6110ed57506000928392509050565b6001600160a01b03831660009081526013602090815260408083206004810154600390910154818552601290935292208054600590910154811580611130575080155b156111445750600096879650945050505050565b60008481526012602052604081208054600590910154611165918691612822565b90506000611173828a611bc9565b9199919850909650505050505050565b600a546000908152601260205260408120600601544311156111b8576040516354a9d44560e01b815260040160405180910390fd5b6111ce336000356001600160e01b03191661286f565b6111ea5760405162461bcd60e51b8152600401610bd39061316d565b60006001600a546111fb91906131a9565b6000818152601260205260409020600381015460048201546005909201549293509161122782846131c0565b8603611237575090949350505050565b60008061124d61124785876131c0565b87612328565b5050505091509150600081111561127d5781156112725761126d816129b2565b61127d565b61127b81612b3f565b505b82600254111561129e5761129e308460025461129991906131a9565b612bf7565b8260025410156112bf576112bf30600254856112ba91906131a9565b612c61565b83601160008282546112d191906131a9565b9091555050600a8690556112e488611d20565b98975050505050505050565b60006001600a541015611316576040516324875a2960e11b815260040160405180910390fd5b600a54600090815260126020526040902060060154431161134a576040516335e7b1ad60e01b815260040160405180910390fd5b610b0e823361245e565b60006001600a54101561137a576040516324875a2960e11b815260040160405180910390fd5b600a5460009081526012602052604090206006015443116113ae576040516335e7b1ad60e01b815260040160405180910390fd5b81836113b982612015565b156113d75760405163915322ab60e01b815260040160405180910390fd5b6009546113e2612cb3565b6113ec90836131c0565b111561140b57604051632d924d8f60e21b815260040160405180910390fd5b600c546001600160a01b0316158015906114975750600c5460405163387777d760e21b81526001600160a01b038481166004830152602482018490529091169063e1dddf5c90604401602060405180830381865afa158015611471573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149591906131d8565b155b156114b55760405163e114baed60e01b815260040160405180910390fd5b836114bf816115f8565b60006114cb878761273a565b90508681106114ed5760405163df19d98160e01b815260040160405180910390fd5b60006114f982896131a9565b9050816011600082825461150d91906131c0565b9091555050600a546001600160a01b038816600090815260136020526040812060018101929092558154839291906115469084906131c0565b9091555050600a546000908152601260205260408120600101805483929061156f9084906131c0565b909155506115aa90506001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481633308b612cd5565b866001600160a01b03167f35db3d768e685509e031bae369804ca7dc6656af739e079f1d3312cadc7b19d8826040516115e591815260200190565b60405180910390a2979650505050505050565b6116018161270a565b15611684576001600160a01b038116600090815260136020526040902080546001918201546116349261043891906131c0565b6001600160a01b0382166000908152601360205260408120600201805490919061165f9084906131c0565b90915550506001600160a01b0381166000908152601360205260408120818155600101555b50565b6006546001600160a01b031633148061171c575060075460405163b700961360e01b81526001600160a01b039091169063b7009613906116db90339030906001600160e01b031960003516906004016131f5565b602060405180830381865afa1580156116f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171c91906131d8565b61172557600080fd5b600780546001600160a01b0319166001600160a01b03831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b6001600160a01b0381166000908152601360209081526040808320815160a081018352815481526001820154938101939093526002810154918301829052600381015460608401526004015460808301526117cb8461270a565b156117f857815160208301516117e791906104389060016131c0565b6117f190826131c0565b9050611807565b815161180490846131c0565b92505b61181081610a87565b61181a90846131c0565b949350505050565b60018054610a0690613133565b6000611839612cb3565b600954610e3791906131a9565b600a54156118675760405163abd5ec5d60e01b815260040160405180910390fd5b61187d336000356001600160e01b03191661286f565b6118995760405162461bcd60e51b8152600401610bd39061316d565b818110156118ba5760405163d6d43aa560e01b815260040160405180910390fd5b81156118e05760006118cb83612d5f565b33600090815260136020526040902060020155505b60098190556001600a819055600854600091825260126020527f71a67924699a20698523213e55fe499d539379d7769cd5567e2c45d583f815a38190556002547f71a67924699a20698523213e55fe499d539379d7769cd5567e2c45d583f815a855437f71a67924699a20698523213e55fe499d539379d7769cd5567e2c45d583f815a9556040517f83fa67c50a5aa34cb3587f7265408d5af55de90232e78550e11a7c22094aa5b79261199c92908252602082015260400190565b60405180910390a15050565b336000908152600360205260408120805483919083906119c99084906131a9565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133906000805160206132f483398151915290610b6f9086815260200190565b600a546000908152601260205260409020600601544311611a42576040516335e7b1ad60e01b815260040160405180910390fd5b601154600003611a65576040516315a6ad9b60e11b815260040160405180910390fd5b601180546000909155600d5460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018390527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489091169063a9059cbb906044016020604051808303816000875af1158015611ae2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0691906131d8565b507fb4d8a8406f3bc5c93ba2220e6aaf8109c1a327ac49dae93247607983598fc59f601154604051611b3a91815260200190565b60405180910390a150565b6002546000908015610aac57610aa7611b5d60085490565b849083612841565b60006001600a541015611b8b576040516324875a2960e11b815260040160405180910390fd5b600a546000908152601260205260409020600601544311611bbf576040516335e7b1ad60e01b815260040160405180910390fd5b610b0e8233611354565b600c546000906001600160a01b031615801590611c4f5750600c546040516350f0e8fd60e11b81526001600160a01b0384811660048301529091169063a1e1d1fa90602401602060405180830381865afa158015611c2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4f91906131d8565b15611cd657600c54604051633c20ee6360e01b81526001600160a01b038481166004830152611ccf921690633c20ee63906024015b602060405180830381865afa158015611ca1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc59190613222565b8490612710612841565b9050610b0e565b600c54600160a01b900460ff16611cee576000611ccf565b601054611ccf908490612710612841565b6002546000908015610aac57610aa781611d1860085490565b859190612822565b6000611d38336000356001600160e01b03191661286f565b611d545760405162461bcd60e51b8152600401610bd39061316d565b6001600a541015611d78576040516324875a2960e11b815260040160405180910390fd5b600080600080600080611d8d88600a54612328565b9550955095509550955095508160116000828254611dab91906131c0565b9091555050600a8054600090815260126020908152604080832060040186905560088590558354835291829020600301849055915481519081529182018390527f83fa67c50a5aa34cb3587f7265408d5af55de90232e78550e11a7c22094aa5b7910160405180910390a1600a8054906000611e268361323b565b90915550508415611e4f578515611e4657611e4085612b3f565b50611e4f565b611e4f856129b2565b8215611e7d578315611e6a57611e6483612d5f565b50611e7d565b611e7b611e7684611cff565b612df2565b505b6009546008541115611ec55760085460098190556040519081527f08a867523cfba7070a600e4f91a0f4794f120cf8c7433ae70c15616acb8945bb9060200160405180910390a15b600854600a805460009081526012602052604080822093909355600254915481529190912060050155600b54611efb90436131c0565b600a5460009081526012602052604090206006015550506008549695505050505050565b611f35336000356001600160e01b03191661286f565b611f515760405162461bcd60e51b8152600401610bd39061316d565b6001600160a01b038116611f785760405163e72c6ebd60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b611fb0336000356001600160e01b03191661286f565b611fcc5760405162461bcd60e51b8152600401610bd39061316d565b612710831180611fdd575061271082115b80611fe9575061271081115b156120075760405163e72c6ebd60e01b815260040160405180910390fd5b600e92909255600f55601055565b6001600160a01b03811660009081526013602052604081206004015415801590610b0e5750600a546001600160a01b038316600090815260136020526040902060040154111592915050565b428410156120b15760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610bd3565b600060016120bd610e06565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa1580156121c9573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906121ff5750876001600160a01b0316816001600160a01b0316145b61223c5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610bd3565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6122bb336000356001600160e01b03191661286f565b6122d75760405162461bcd60e51b8152600401610bd39061316d565b801515600c60149054906101000a900460ff1615150361230a5760405163e72c6ebd60e01b815260040160405180910390fd5b600c8054911515600160a01b0260ff60a01b19909216919091179055565b6000806000806000806000600a54881461235357600088815260126020526040902060050154612357565b6002545b6000898152601260205260409020805460018201546002909201549293509182158015612384575060008c115b156123a257604051634ce0b10960e11b815260040160405180910390fd5b6123ac838d612e87565b95506123b8868d6131a9565b9450600084156123d2576123cd828787612822565b6123d4565b815b90508083119850886123ef576123ea83826131a9565b6123f9565b6123f981846131a9565b97508815612433578787111561241e5761241388886131a9565b995060009a50612444565b61242887896131a9565b995060019a50612444565b61243d87896131c0565b995060009a505b50505050509295509295509295565b6000610b0e82611cff565b60006001600a541015612484576040516324875a2960e11b815260040160405180910390fd5b600a5460009081526012602052604090206006015443116124b8576040516335e7b1ad60e01b815260040160405180910390fd5b818360018110156124dc576040516373acfc1d60e11b815260040160405180910390fd5b336001600160a01b0383161461254a576001600160a01b038216600090815260046020908152604080832033845290915290205460001981146125485761252382826131a9565b6001600160a01b03841660009081526004602090815260408083203384529091529020555b505b61255382612015565b1561257157604051634eda1c7f60e11b815260040160405180910390fd5b8361257b816115f8565b6001600160a01b0385166000908152601360205260409020600301546125a29087906131c0565b6001600160a01b03861660009081526013602052604090206002015410156125dd5760405163429d082960e01b815260040160405180910390fd5b6001600160a01b038516600090815260136020526040812060030180548892906126089084906131c0565b9091555050600a5461261b9060016131c0565b6001600160a01b038616600090815260136020908152604080832060040193909355600a54825260129052908120600201805488929061265c9084906131c0565b90915550506040518681526001600160a01b038616907fd1a803266af0ed3665ccaf50b07ec253d106678a2ee563fbc5a0502d702119ae9060200160405180910390a25093949350505050565b6126bf336000356001600160e01b03191661286f565b6126db5760405162461bcd60e51b8152600401610bd39061316d565b6126e3612cb3565b60095410156127055760405163d6d43aa560e01b815260040160405180910390fd5b600955565b6001600160a01b0381166000908152601360205260408120600101548015801590610aae5750600a541192915050565b600c546000906001600160a01b0316158015906127c05750600c546040516350f0e8fd60e11b81526001600160a01b0384811660048301529091169063a1e1d1fa90602401602060405180830381865afa15801561279c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c091906131d8565b156127f957600c5460405163490a847b60e11b81526001600160a01b038481166004830152611ccf92169063921508f690602401611c84565b600c54600160a01b900460ff16612811576000611ccf565b600f54611ccf908490612710612841565b82820281151584158583048514171661283a57600080fd5b0492915050565b82820281151584158583048514171661285957600080fd5b6001826001830304018115150290509392505050565b6007546000906001600160a01b031680158015906128f9575060405163b700961360e01b81526001600160a01b0382169063b7009613906128b8908790309088906004016131f5565b602060405180830381865afa1580156128d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f991906131d8565b8061181a57506006546001600160a01b03858116911614949350505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161294a9190613254565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b604051636eb1769f60e11b815233600482015230602482015281907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03169063dd62ed3e90604401602060405180830381865afa158015612a1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a429190613222565b1015612a61576040516376dbb4e960e01b815260040160405180910390fd5b6040516370a0823160e01b815233600482015281907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316906370a0823190602401602060405180830381865afa158015612ac7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aeb9190613222565b1015612b0a5760405163b2a196b160e01b815260040160405180910390fd5b6116846001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816333084612cd5565b60405163a9059cbb60e01b81526001600160a01b037f0000000000000000000000004457df4a5bccf796662b6374d5947c881cc83ac781166004830152602482018390526000917f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489091169063a9059cbb906044016020604051808303816000875af1158015612bd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0e91906131d8565b6001600160a01b03821660009081526003602052604081208054839290612c1f9084906131a9565b90915550506002805482900390556040518181526000906001600160a01b038416906000805160206132f4833981519152906020015b60405180910390a35050565b8060026000828254612c7391906131c0565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481526000805160206132f48339815191529101612c55565b600a54600090815260126020526040812060010154600854610e3791906131c0565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080612d585760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610bd3565b5050505050565b600080612d6b83612453565b905080600003612d8e57604051637a740e7b60e01b815260040160405180910390fd5b612d983082612c61565b8260086000828254612daa91906131c0565b90915550506040805184815260208101839052309133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a392915050565b600080612dfe83610ec4565b905080600003612e21576040516324a0f01560e21b815260040160405180910390fd5b612e2b3084612bf7565b8060086000828254612e3d91906131a9565b909155505060408051828152602081018590523091829133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a492915050565b600c54600090600160a01b900460ff16612ea357506000610b0e565b8282118015612eb457506000600e54115b612ebf576000610b0b565b610b0b612ecc84846131a9565b600e5490612710612841565b600060208083528351808285015260005b81811015612f0557858101830151858201604001528201612ee9565b81811115612f17576000604083870101525b50601f01601f1916929092016040019392505050565b600060208284031215612f3f57600080fd5b5035919050565b60008060408385031215612f5957600080fd5b50508035926020909101359150565b6001600160a01b038116811461168457600080fd5b60008060408385031215612f9057600080fd5b8235612f9b81612f68565b946020939093013593505050565b600060208284031215612fbb57600080fd5b8135610aae81612f68565b600080600060608486031215612fdb57600080fd5b8335612fe681612f68565b92506020840135612ff681612f68565b929592945050506040919091013590565b6000806040838503121561301a57600080fd5b82359150602083013561302c81612f68565b809150509250929050565b60008060006060848603121561304c57600080fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a03121561307e57600080fd5b873561308981612f68565b9650602088013561309981612f68565b95506040880135945060608801359350608088013560ff811681146130bd57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b801515811461168457600080fd5b6000602082840312156130fa57600080fd5b8135610aae816130da565b6000806040838503121561311857600080fd5b823561312381612f68565b9150602083013561302c81612f68565b600181811c9082168061314757607f821691505b60208210810361316757634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b6000828210156131bb576131bb613193565b500390565b600082198211156131d3576131d3613193565b500190565b6000602082840312156131ea57600080fd5b8151610aae816130da565b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b60006020828403121561323457600080fd5b5051919050565b60006001820161324d5761324d613193565b5060010190565b600080835481600182811c91508083168061327057607f831692505b6020808410820361328f57634e487b7160e01b86526022600452602486fd5b8180156132a357600181146132b8576132e5565b60ff19861689528415158502890196506132e5565b60008a81526020902060005b868110156132dd5781548b8201529085019083016132c4565b505084890196505b50949897505050505050505056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122030265c565d620ec18d93a75d7fb591d36fcac1ed8706ffd2e0bbb067925b959364736f6c634300080f0033

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  ]

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.