Contract 0x00000008786611c72A00909bD8d398b1bE195Be3

 
Txn Hash
Method
Block
From
To
Value
0xda5c2ff25f8becc6bbf8cb75a3e8c513644bf6fd05dac6b48af9390dbe7fe3ffUpdate Pending D...156358612022-09-29 1:56:477 hrs 18 mins agoENS Name 00d1.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00057733 11.09926195
0x210a826224f38b8c75fb2d17df85498b7948a12e293c5abf95c9c96df2d59decUnlock156225472022-09-27 5:16:112 days 3 hrs ago0x80eb3b50f86932a635c286e461c9f0e400a7fcb2 IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00067835 8.17372707
0xefd18bbe242cee513d6b9dd1769f75e08c5a4e6790423f958fafaf7d7c1baed7Unlock155890992022-09-22 13:12:116 days 20 hrs ago0xb7ea1aaae9f409e0b58ccd2372adc0da792e9e94 IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00077541 9.34323617
0xe33782b4b252558b0f7dd3225b384e7b1845222ec15bf9a5550d19c1b1797c6dUpdate Pending D...155890962022-09-22 13:11:356 days 20 hrs ago0xb7ea1aaae9f409e0b58ccd2372adc0da792e9e94 IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00051478 9.89658487
0xa6469466bb17f6d05ba8894ec50f5b5904186ec42945f3cfa739b14b58f2181aDeposit155681732022-09-19 14:32:239 days 18 hrs ago0x0c0f1a11b220fd1d53306f67b9327cdeb31d662b IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00239712 15.48389755
0xa977d1ce687eb4b4626ecca9920e1f8a22969d0e2b07a2aa5f8fbabf26d9554bUpdate Pending D...155210142022-09-12 13:05:0516 days 20 hrs ago0x0c9ccbada1411687f6ffa7df317af35b16b1fe0c IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00059464 24.65161634
0xced59b8e87f78567afc482778ee9fa98e15caf84db5004e49eb2413331b4f6f8Update Pending D...155146132022-09-11 11:37:4117 days 21 hrs ago0x80eb3b50f86932a635c286e461c9f0e400a7fcb2 IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00027875 5.36023109
0x0c555f41d0addd26d65d83b74ec3aec7be495241fb80740437a1ddb49ba9a259Update Pending D...155103782022-09-10 18:48:1118 days 14 hrs agoENS Name qsteak.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00021086 8.74143926
0xe8b9dc5d21f6666e48efe6ab9602d9a769c40de5a22de08c16dfec54c5f0947cUnlock154983022022-09-08 19:02:5820 days 14 hrs agoENS Name refi401k.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00182247 39.35374123
0x2be7f7a6ce3d9b56fd2c7d2229a5379b8003b8f202e28594f3cd563cf7f1a7f4Withdraw154912322022-09-07 15:59:2521 days 17 hrs agoENS Name hufhaus.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00300091 35.79759868
0xddadbf29ac844c6e6abf61f368c5f33502fc8634839e5ac56d761817c3aaa2e1Unlock154906282022-09-07 13:43:4921 days 19 hrs agoENS Name bitinvestia.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00119812 14.43450544
0x051daf93209e152642aabad1967c043958f2e4e95ef07ebc6442029bff36be48Update Pending D...154906242022-09-07 13:43:0221 days 19 hrs agoENS Name bitinvestia.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00078386 15.06962999
0x00e911a03514c6f86e9073f51145c5c1c8686e97ff7ffdf9b7e65fb761662234Deposit154903642022-09-07 12:44:3821 days 20 hrs agoENS Name klok.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00302066 15.98017892
0x843c3cf728d5b7b55a8e473ee89bd0e19eff3a08b180dccef114c3193ca653c4Unlock154872682022-09-07 0:41:3522 days 8 hrs agoENS Name girlfromnowhere.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00141999 17.1099824
0xc3688e1246c344c685f2af9c82b2ac11d1b19d6be37e9c8a84cb86e376c3b3d8Withdraw154872592022-09-07 0:40:2622 days 8 hrs agoENS Name girlfromnowhere.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00187231 17.70844871
0x5044c60b8d75f046118d88c308696d78a5d3a4e3166dd6cdc8f9ee8712ed4872Unlock154866642022-09-06 22:22:1122 days 10 hrs agoENS Name refi401k.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00084175 10.14411019
0x6d62497b43e770b246f5d3bf2f915e5b09627a0c7250de6c384a62897784dbf8Withdraw154789752022-09-05 16:58:4223 days 16 hrs ago0x0c9ccbada1411687f6ffa7df317af35b16b1fe0c IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00065764 22.86092126
0xbde10f5447fe82a11c75a016f2856ea4450e8f3fe738917d4665a9f5a09e8a6cUnlock154789702022-09-05 16:57:0223 days 16 hrs ago0x0c9ccbada1411687f6ffa7df317af35b16b1fe0c IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00228326 22.81437527
0x32b93a29028ce9241f46ef54a70c7e80d8142059c55b52359389751a53081cfaWithdraw154789442022-09-05 16:50:2323 days 16 hrs ago0x0c9ccbada1411687f6ffa7df317af35b16b1fe0c IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00206271 23.27330748
0xe1ea4de6bb621aae8a579372cbe6fa892057368c6d0292759780a9360e07f666Unlock154788132022-09-05 16:17:2823 days 16 hrs ago0x0c9ccbada1411687f6ffa7df317af35b16b1fe0c IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00060514 20.75966813
0x5430ea0cc14251d9bb1bfdb6b6e6b0f7eeeb3ba4a345d31401fb4bbe2e7a41d1Withdraw154780772022-09-05 13:15:5123 days 19 hrs ago0x0c9ccbada1411687f6ffa7df317af35b16b1fe0c IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00052395 20.35327073
0xf6893aef7682064326c36460a3108162f8b3605e6fcf5e7f7195c600f54dc153Update Pending D...154729532022-09-04 17:35:1124 days 15 hrs agoENS Name girlfromnowhere.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.0002866 8.04785031
0xd2957fe3bfb63ec53b0bbdd28bd4c329d7c1acff4af312da27ffdbb9ab8baf48Progress Epoch154728052022-09-04 17:03:2024 days 16 hrs ago0x4457df4a5bccf796662b6374d5947c881cc83ac7 IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00272502 14.12430178
0x581665637ad7a912bd4003e833671acdaf989c75d4ed9836b17073b4fff191ecUnlock154721952022-09-04 14:41:4224 days 18 hrs agoENS Name hufhaus.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00120296 14.49282084
0x4b4ea4afcd4ae610ac354d1ad2b32fd7b8a3b1442bf2c2193b4ea363badc0e5eSend Fee154600532022-09-02 15:49:1126 days 17 hrs agoENS Name 00d1.eth IN  0x00000008786611c72a00909bd8d398b1be195be30 Ether0.00073212 12.39482287
[ Download CSV Export 
View more zero value Internal Transactions in Advanced View mode
Loading

Similar Match Source Code
Note: This contract matches the deployed ByteCode of the Source Code for Contract 0x00000089e12704ffab70374b78ef44fc16ccd583

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

[{"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"}]

610120604052600060088190556009819055600a55611770600b55600c805460ff60a01b191690556107d0600e556064600f8190556010553480156200004457600080fd5b50604051620039ee380380620039ee833981016040819052620000679162000370565b8360008388888181846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000ae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000d4919062000423565b6000620000e28482620004de565b506001620000f18382620004de565b5060ff81166080524660a05262000107620001ea565b60c0525050506001600160a01b0392831660e0525050600680548483166001600160a01b0319918216811790925560078054938516939091169290921790915560405133907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7690600090a36040516001600160a01b0382169033907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350506001600160a01b0393841661010052600d80549385166001600160a01b0319948516179055600c8054919094169216919091179091555062000628915050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516200021e9190620005aa565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620002ae57600080fd5b81516001600160401b0380821115620002cb57620002cb62000286565b604051601f8301601f19908116603f01168101908282118183101715620002f657620002f662000286565b816040528381526020925086838588010111156200031357600080fd5b600091505b8382101562000337578582018301518183018401529082019062000318565b83821115620003495760008385830101525b9695505050505050565b80516001600160a01b03811681146200036b57600080fd5b919050565b60008060008060008060c087890312156200038a57600080fd5b86516001600160401b0380821115620003a257600080fd5b620003b08a838b016200029c565b97506020890151915080821115620003c757600080fd5b50620003d689828a016200029c565b955050620003e76040880162000353565b9350620003f76060880162000353565b9250620004076080880162000353565b91506200041760a0880162000353565b90509295509295509295565b6000602082840312156200043657600080fd5b815160ff811681146200044857600080fd5b9392505050565b600181811c908216806200046457607f821691505b6020821081036200048557634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620004d957600081815260208120601f850160051c81016020861015620004b45750805b601f850160051c820191505b81811015620004d557828155600101620004c0565b5050505b505050565b81516001600160401b03811115620004fa57620004fa62000286565b62000512816200050b84546200044f565b846200048b565b602080601f8311600181146200054a5760008415620005315750858301515b600019600386901b1c1916600185901b178555620004d5565b600085815260208120601f198616915b828110156200057b578886015182559484019460019091019084016200055a565b50858210156200059a5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000808354620005ba816200044f565b60018281168015620005d55760018114620005eb576200061c565b60ff19841687528215158302870194506200061c565b8760005260208060002060005b85811015620006135781548a820152908401908201620005f8565b50505082870194505b50929695505050505050565b60805160a05160c05160e05161010051613349620006a5600039600081816109270152612b5601526000818161061e015281816110120152818161158201528181611a97015281816129cf01528181612a7801528181612b170152612b8801526000610e3f01526000610e0a015260006105c101526133496000f3fe608060405234801561001057600080fd5b50600436106103e65760003560e01c80637a9e5e4b1161020a578063c1dbbc3111610125578063d811fcf0116100b8578063ef8b30f711610087578063ef8b30f71461099a578063f1d2ec1d146109ad578063f366d950146109c0578063f70881a3146109d3578063fa37a9ba146109e657600080fd5b8063d811fcf014610922578063dc4a5bff14610949578063dd62ed3e1461095c578063ecda74cf1461098757600080fd5b8063cec10c11116100f4578063cec10c11146108e0578063d49e271d146108f3578063d4d4be2f146108fc578063d505accf1461090f57600080fd5b8063c1dbbc3114610894578063c6e6f592146108a7578063c7ba9c72146108ba578063ccfc2e8d146108cd57600080fd5b806399f2dda61161019d578063ae39279f1161016c578063ae39279f14610853578063b3d7f6b91461085b578063b6b55f251461086e578063bf7e214f1461088157600080fd5b806399f2dda61461081c578063a4dbc6a814610825578063a5cc958d1461082d578063a9059cbb1461084057600080fd5b80638da5cb5b116101d95780638da5cb5b146107e5578063900cf0cf146107f8578063907e98cd1461080157806395d89b411461081457600080fd5b80637a9e5e4b146107305780637ecebe0014610743578063808dd5eb14610763578063849efb711461078057600080fd5b8063344de0ac11610305578063578334ee1161029857806361bc2e331161026757806361bc2e33146106ce5780636e553f65146106e15780636ea97367146106f457806370a082311461070757806371634e321461072757600080fd5b8063578334ee1461069657806357b17a521461069f5780635a157a25146106a85780636198e339146106bb57600080fd5b80633ccfd60b116102d45780633ccfd60b146106405780634cdad5061461064857806351cff8d91461065b57806356582bf91461066e57600080fd5b8063344de0ac146105f55780633644e515146106085780633813c35a1461061057806338d52e0f1461061957600080fd5b80630d43e8ad1161037d57806318160ddd1161034c57806318160ddd146105585780631c4ad9151461056157806323b872dd146105a9578063313ce567146105bc57600080fd5b80630d43e8ad146104865780630e66dc69146104b1578063119435a4146104c557806313af40351461054557600080fd5b8063095ea7b3116103b9578063095ea7b31461043d5780630a28a477146104605780630ab51bac146104735780630b38bed91461047c57600080fd5b806301e1d114146103eb57806306fdde031461040257806307a2d13a146104175780630807ac571461042a575b600080fd5b6008545b6040519081526020015b60405180910390f35b61040a6109f9565b6040516103f99190612ed8565b6103ef610425366004612f2d565b610a87565b6103ef610438366004612f46565b610ab5565b61045061044b366004612f7d565b610b14565b60405190151581526020016103f9565b6103ef61046e366004612f2d565b610b80565b6103ef60085481565b610484610ba1565b005b600d54610499906001600160a01b031681565b6040516001600160a01b0390911681526020016103f9565b600c5461045090600160a01b900460ff1681565b6105106104d3366004612f2d565b6012602052600090815260409020805460018201546002830154600384015460048501546005860154600690960154949593949293919290919087565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016103f9565b610484610553366004612fa9565b610c2b565b6103ef60025481565b61057461056f366004612f2d565b610ca9565b6040805196151587526020870195909552921515938501939093526060840152608083019190915260a082015260c0016103f9565b6104506105b7366004612fc6565b610cd2565b6105e37f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016103f9565b610484610603366004612fa9565b610db2565b6103ef610e06565b6103ef600e5481565b6104997f000000000000000000000000000000000000000000000000000000000000000081565b6103ef610e61565b6103ef610656366004612f2d565b610ec4565b6103ef610669366004612fa9565b610ecf565b61068161067c366004612fa9565b6110d2565b604080519283526020830191909152016103f9565b6103ef600f5481565b6103ef60105481565b6103ef6106b6366004612f2d565b611183565b6103ef6106c9366004612f2d565b6112f0565b600c54610499906001600160a01b031681565b6103ef6106ef366004613007565b611354565b610484610702366004612fa9565b6115f8565b6103ef610715366004612fa9565b60036020526000908152604090205481565b6103ef60115481565b61048461073e366004612fa9565b611687565b6103ef610751366004612fa9565b60056020526000908152604090205481565b600a54600090815260126020526040902060060154431115610450565b6107bd61078e366004612fa9565b601360205260009081526040902080546001820154600283015460038401546004909401549293919290919085565b604080519586526020860194909452928401919091526060830152608082015260a0016103f9565b600654610499906001600160a01b031681565b6103ef600a5481565b6103ef61080f366004612fa9565b611771565b61040a611822565b6103ef60095481565b6103ef61182f565b61048461083b366004612f46565b611846565b61045061084e366004612f7d565b6119a8565b610484611a0e565b6103ef610869366004612f2d565b611b45565b6103ef61087c366004612f2d565b611b65565b600754610499906001600160a01b031681565b6103ef6108a2366004613007565b611bc9565b6103ef6108b5366004612f2d565b611cff565b6103ef6108c8366004612f2d565b611d20565b6104846108db366004612fa9565b611f1f565b6104846108ee366004613037565b611f9a565b6103ef600b5481565b61045061090a366004612fa9565b612015565b61048461091d366004613063565b612061565b6104997f000000000000000000000000000000000000000000000000000000000000000081565b6104846109573660046130e8565b6122a5565b6103ef61096a366004613105565b600460209081526000928352604080842090915290825290205481565b610574610995366004612f46565b612328565b6103ef6109a8366004612f2d565b612453565b6103ef6109bb366004613007565b61245e565b6104846109ce366004612f2d565b6126a9565b6104506109e1366004612fa9565b61270a565b6103ef6109f4366004613007565b61273a565b60008054610a0690613133565b80601f0160208091040260200160405190810160405280929190818152602001828054610a3290613133565b8015610a7f5780601f10610a5457610100808354040283529160200191610a7f565b820191906000526020600020905b815481529060010190602001808311610a6257829003601f168201915b505050505081565b6002546000908015610aac57610aa7610a9f60085490565b849083612822565b610aae565b825b9392505050565b6000818152601260205260408120600501541580610adf5750600082815260126020526040902054155b15610aeb575081610b0e565b600082815260126020526040902060058101549054610b0b918591612822565b90505b92915050565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610b6f9086815260200190565b60405180910390a350600192915050565b6002546000908015610aac57610aa781610b9960085490565b859190612841565b610bb7336000356001600160e01b03191661286f565b610bdc5760405162461bcd60e51b8152600401610bd39061316d565b60405180910390fd5b600a54600090815260126020526040902060060154431115610c11576040516354a9d44560e01b815260040160405180910390fd5b600a54600090815260126020526040902043600690910155565b610c41336000356001600160e01b03191661286f565b610c5d5760405162461bcd60e51b8152600401610bd39061316d565b600680546001600160a01b0319166001600160a01b03831690811790915560405133907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7690600090a350565b600080600080600080610cbe87600a54612328565b949c939b5091995097509550909350915050565b6001600160a01b03831660009081526004602090815260408083203384529091528120546000198114610d2e57610d0983826131a9565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b03851660009081526003602052604081208054859290610d569084906131a9565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716906000805160206132f483398151915290610d9f9087815260200190565b60405180910390a3506001949350505050565b610dc8336000356001600160e01b03191661286f565b610de45760405162461bcd60e51b8152600401610bd39061316d565b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b60007f00000000000000000000000000000000000000000000000000000000000000004614610e3c57610e37612918565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b60006001600a541015610e87576040516324875a2960e11b815260040160405180910390fd5b600a546000908152601260205260409020600601544311610ebb576040516335e7b1ad60e01b815260040160405180910390fd5b610e3733610ecf565b6000610b0e82610a87565b60006001600a541015610ef5576040516324875a2960e11b815260040160405180910390fd5b600a546000908152601260205260409020600601544311610f29576040516335e7b1ad60e01b815260040160405180910390fd5b33610f3381612015565b610f50576040516377a59ab560e11b815260040160405180910390fd5b33610f5a816115f8565b600080610f66336110d2565b33600090815260136020526040812060038101546002909101805494965092945092610f939084906131a9565b90915550503360009081526013602052604081206003810182905560040181905560118054839290610fc69084906131c0565b9091555050818103610fdd576000945050506110cb565b6000610fe982846131a9565b60405163a9059cbb60e01b81526001600160a01b038981166004830152602482018390529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af115801561105d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108191906131d8565b50604080516001600160a01b03891681526020810183905233917f6985a6dd52aeb8194df40b7af2f362f362440affc39c1314649abc28dbf6b628910160405180910390a2945050505b5050919050565b6000806110de83612015565b6110ed57506000928392509050565b6001600160a01b03831660009081526013602090815260408083206004810154600390910154818552601290935292208054600590910154811580611130575080155b156111445750600096879650945050505050565b60008481526012602052604081208054600590910154611165918691612822565b90506000611173828a611bc9565b9199919850909650505050505050565b600a546000908152601260205260408120600601544311156111b8576040516354a9d44560e01b815260040160405180910390fd5b6111ce336000356001600160e01b03191661286f565b6111ea5760405162461bcd60e51b8152600401610bd39061316d565b60006001600a546111fb91906131a9565b6000818152601260205260409020600381015460048201546005909201549293509161122782846131c0565b8603611237575090949350505050565b60008061124d61124785876131c0565b87612328565b5050505091509150600081111561127d5781156112725761126d816129b2565b61127d565b61127b81612b3f565b505b82600254111561129e5761129e308460025461129991906131a9565b612bf7565b8260025410156112bf576112bf30600254856112ba91906131a9565b612c61565b83601160008282546112d191906131a9565b9091555050600a8690556112e488611d20565b98975050505050505050565b60006001600a541015611316576040516324875a2960e11b815260040160405180910390fd5b600a54600090815260126020526040902060060154431161134a576040516335e7b1ad60e01b815260040160405180910390fd5b610b0e823361245e565b60006001600a54101561137a576040516324875a2960e11b815260040160405180910390fd5b600a5460009081526012602052604090206006015443116113ae576040516335e7b1ad60e01b815260040160405180910390fd5b81836113b982612015565b156113d75760405163915322ab60e01b815260040160405180910390fd5b6009546113e2612cb3565b6113ec90836131c0565b111561140b57604051632d924d8f60e21b815260040160405180910390fd5b600c546001600160a01b0316158015906114975750600c5460405163387777d760e21b81526001600160a01b038481166004830152602482018490529091169063e1dddf5c90604401602060405180830381865afa158015611471573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149591906131d8565b155b156114b55760405163e114baed60e01b815260040160405180910390fd5b836114bf816115f8565b60006114cb878761273a565b90508681106114ed5760405163df19d98160e01b815260040160405180910390fd5b60006114f982896131a9565b9050816011600082825461150d91906131c0565b9091555050600a546001600160a01b038816600090815260136020526040812060018101929092558154839291906115469084906131c0565b9091555050600a546000908152601260205260408120600101805483929061156f9084906131c0565b909155506115aa90506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308b612cd5565b866001600160a01b03167f35db3d768e685509e031bae369804ca7dc6656af739e079f1d3312cadc7b19d8826040516115e591815260200190565b60405180910390a2979650505050505050565b6116018161270a565b15611684576001600160a01b038116600090815260136020526040902080546001918201546116349261043891906131c0565b6001600160a01b0382166000908152601360205260408120600201805490919061165f9084906131c0565b90915550506001600160a01b0381166000908152601360205260408120818155600101555b50565b6006546001600160a01b031633148061171c575060075460405163b700961360e01b81526001600160a01b039091169063b7009613906116db90339030906001600160e01b031960003516906004016131f5565b602060405180830381865afa1580156116f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171c91906131d8565b61172557600080fd5b600780546001600160a01b0319166001600160a01b03831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b6001600160a01b0381166000908152601360209081526040808320815160a081018352815481526001820154938101939093526002810154918301829052600381015460608401526004015460808301526117cb8461270a565b156117f857815160208301516117e791906104389060016131c0565b6117f190826131c0565b9050611807565b815161180490846131c0565b92505b61181081610a87565b61181a90846131c0565b949350505050565b60018054610a0690613133565b6000611839612cb3565b600954610e3791906131a9565b600a54156118675760405163abd5ec5d60e01b815260040160405180910390fd5b61187d336000356001600160e01b03191661286f565b6118995760405162461bcd60e51b8152600401610bd39061316d565b818110156118ba5760405163d6d43aa560e01b815260040160405180910390fd5b81156118e05760006118cb83612d5f565b33600090815260136020526040902060020155505b60098190556001600a819055600854600091825260126020527f71a67924699a20698523213e55fe499d539379d7769cd5567e2c45d583f815a38190556002547f71a67924699a20698523213e55fe499d539379d7769cd5567e2c45d583f815a855437f71a67924699a20698523213e55fe499d539379d7769cd5567e2c45d583f815a9556040517f83fa67c50a5aa34cb3587f7265408d5af55de90232e78550e11a7c22094aa5b79261199c92908252602082015260400190565b60405180910390a15050565b336000908152600360205260408120805483919083906119c99084906131a9565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133906000805160206132f483398151915290610b6f9086815260200190565b600a546000908152601260205260409020600601544311611a42576040516335e7b1ad60e01b815260040160405180910390fd5b601154600003611a65576040516315a6ad9b60e11b815260040160405180910390fd5b601180546000909155600d5460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015611ae2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0691906131d8565b507fb4d8a8406f3bc5c93ba2220e6aaf8109c1a327ac49dae93247607983598fc59f601154604051611b3a91815260200190565b60405180910390a150565b6002546000908015610aac57610aa7611b5d60085490565b849083612841565b60006001600a541015611b8b576040516324875a2960e11b815260040160405180910390fd5b600a546000908152601260205260409020600601544311611bbf576040516335e7b1ad60e01b815260040160405180910390fd5b610b0e8233611354565b600c546000906001600160a01b031615801590611c4f5750600c546040516350f0e8fd60e11b81526001600160a01b0384811660048301529091169063a1e1d1fa90602401602060405180830381865afa158015611c2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4f91906131d8565b15611cd657600c54604051633c20ee6360e01b81526001600160a01b038481166004830152611ccf921690633c20ee63906024015b602060405180830381865afa158015611ca1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc59190613222565b8490612710612841565b9050610b0e565b600c54600160a01b900460ff16611cee576000611ccf565b601054611ccf908490612710612841565b6002546000908015610aac57610aa781611d1860085490565b859190612822565b6000611d38336000356001600160e01b03191661286f565b611d545760405162461bcd60e51b8152600401610bd39061316d565b6001600a541015611d78576040516324875a2960e11b815260040160405180910390fd5b600080600080600080611d8d88600a54612328565b9550955095509550955095508160116000828254611dab91906131c0565b9091555050600a8054600090815260126020908152604080832060040186905560088590558354835291829020600301849055915481519081529182018390527f83fa67c50a5aa34cb3587f7265408d5af55de90232e78550e11a7c22094aa5b7910160405180910390a1600a8054906000611e268361323b565b90915550508415611e4f578515611e4657611e4085612b3f565b50611e4f565b611e4f856129b2565b8215611e7d578315611e6a57611e6483612d5f565b50611e7d565b611e7b611e7684611cff565b612df2565b505b6009546008541115611ec55760085460098190556040519081527f08a867523cfba7070a600e4f91a0f4794f120cf8c7433ae70c15616acb8945bb9060200160405180910390a15b600854600a805460009081526012602052604080822093909355600254915481529190912060050155600b54611efb90436131c0565b600a5460009081526012602052604090206006015550506008549695505050505050565b611f35336000356001600160e01b03191661286f565b611f515760405162461bcd60e51b8152600401610bd39061316d565b6001600160a01b038116611f785760405163e72c6ebd60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b611fb0336000356001600160e01b03191661286f565b611fcc5760405162461bcd60e51b8152600401610bd39061316d565b612710831180611fdd575061271082115b80611fe9575061271081115b156120075760405163e72c6ebd60e01b815260040160405180910390fd5b600e92909255600f55601055565b6001600160a01b03811660009081526013602052604081206004015415801590610b0e5750600a546001600160a01b038316600090815260136020526040902060040154111592915050565b428410156120b15760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610bd3565b600060016120bd610e06565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa1580156121c9573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906121ff5750876001600160a01b0316816001600160a01b0316145b61223c5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610bd3565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6122bb336000356001600160e01b03191661286f565b6122d75760405162461bcd60e51b8152600401610bd39061316d565b801515600c60149054906101000a900460ff1615150361230a5760405163e72c6ebd60e01b815260040160405180910390fd5b600c8054911515600160a01b0260ff60a01b19909216919091179055565b6000806000806000806000600a54881461235357600088815260126020526040902060050154612357565b6002545b6000898152601260205260409020805460018201546002909201549293509182158015612384575060008c115b156123a257604051634ce0b10960e11b815260040160405180910390fd5b6123ac838d612e87565b95506123b8868d6131a9565b9450600084156123d2576123cd828787612822565b6123d4565b815b90508083119850886123ef576123ea83826131a9565b6123f9565b6123f981846131a9565b97508815612433578787111561241e5761241388886131a9565b995060009a50612444565b61242887896131a9565b995060019a50612444565b61243d87896131c0565b995060009a505b50505050509295509295509295565b6000610b0e82611cff565b60006001600a541015612484576040516324875a2960e11b815260040160405180910390fd5b600a5460009081526012602052604090206006015443116124b8576040516335e7b1ad60e01b815260040160405180910390fd5b818360018110156124dc576040516373acfc1d60e11b815260040160405180910390fd5b336001600160a01b0383161461254a576001600160a01b038216600090815260046020908152604080832033845290915290205460001981146125485761252382826131a9565b6001600160a01b03841660009081526004602090815260408083203384529091529020555b505b61255382612015565b1561257157604051634eda1c7f60e11b815260040160405180910390fd5b8361257b816115f8565b6001600160a01b0385166000908152601360205260409020600301546125a29087906131c0565b6001600160a01b03861660009081526013602052604090206002015410156125dd5760405163429d082960e01b815260040160405180910390fd5b6001600160a01b038516600090815260136020526040812060030180548892906126089084906131c0565b9091555050600a5461261b9060016131c0565b6001600160a01b038616600090815260136020908152604080832060040193909355600a54825260129052908120600201805488929061265c9084906131c0565b90915550506040518681526001600160a01b038616907fd1a803266af0ed3665ccaf50b07ec253d106678a2ee563fbc5a0502d702119ae9060200160405180910390a25093949350505050565b6126bf336000356001600160e01b03191661286f565b6126db5760405162461bcd60e51b8152600401610bd39061316d565b6126e3612cb3565b60095410156127055760405163d6d43aa560e01b815260040160405180910390fd5b600955565b6001600160a01b0381166000908152601360205260408120600101548015801590610aae5750600a541192915050565b600c546000906001600160a01b0316158015906127c05750600c546040516350f0e8fd60e11b81526001600160a01b0384811660048301529091169063a1e1d1fa90602401602060405180830381865afa15801561279c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c091906131d8565b156127f957600c5460405163490a847b60e11b81526001600160a01b038481166004830152611ccf92169063921508f690602401611c84565b600c54600160a01b900460ff16612811576000611ccf565b600f54611ccf908490612710612841565b82820281151584158583048514171661283a57600080fd5b0492915050565b82820281151584158583048514171661285957600080fd5b6001826001830304018115150290509392505050565b6007546000906001600160a01b031680158015906128f9575060405163b700961360e01b81526001600160a01b0382169063b7009613906128b8908790309088906004016131f5565b602060405180830381865afa1580156128d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f991906131d8565b8061181a57506006546001600160a01b03858116911614949350505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161294a9190613254565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b604051636eb1769f60e11b815233600482015230602482015281907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dd62ed3e90604401602060405180830381865afa158015612a1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a429190613222565b1015612a61576040516376dbb4e960e01b815260040160405180910390fd5b6040516370a0823160e01b815233600482015281907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612ac7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aeb9190613222565b1015612b0a5760405163b2a196b160e01b815260040160405180910390fd5b6116846001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333084612cd5565b60405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018390526000917f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015612bd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0e91906131d8565b6001600160a01b03821660009081526003602052604081208054839290612c1f9084906131a9565b90915550506002805482900390556040518181526000906001600160a01b038416906000805160206132f4833981519152906020015b60405180910390a35050565b8060026000828254612c7391906131c0565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481526000805160206132f48339815191529101612c55565b600a54600090815260126020526040812060010154600854610e3791906131c0565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080612d585760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610bd3565b5050505050565b600080612d6b83612453565b905080600003612d8e57604051637a740e7b60e01b815260040160405180910390fd5b612d983082612c61565b8260086000828254612daa91906131c0565b90915550506040805184815260208101839052309133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a392915050565b600080612dfe83610ec4565b905080600003612e21576040516324a0f01560e21b815260040160405180910390fd5b612e2b3084612bf7565b8060086000828254612e3d91906131a9565b909155505060408051828152602081018590523091829133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a492915050565b600c54600090600160a01b900460ff16612ea357506000610b0e565b8282118015612eb457506000600e54115b612ebf576000610b0b565b610b0b612ecc84846131a9565b600e5490612710612841565b600060208083528351808285015260005b81811015612f0557858101830151858201604001528201612ee9565b81811115612f17576000604083870101525b50601f01601f1916929092016040019392505050565b600060208284031215612f3f57600080fd5b5035919050565b60008060408385031215612f5957600080fd5b50508035926020909101359150565b6001600160a01b038116811461168457600080fd5b60008060408385031215612f9057600080fd5b8235612f9b81612f68565b946020939093013593505050565b600060208284031215612fbb57600080fd5b8135610aae81612f68565b600080600060608486031215612fdb57600080fd5b8335612fe681612f68565b92506020840135612ff681612f68565b929592945050506040919091013590565b6000806040838503121561301a57600080fd5b82359150602083013561302c81612f68565b809150509250929050565b60008060006060848603121561304c57600080fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a03121561307e57600080fd5b873561308981612f68565b9650602088013561309981612f68565b95506040880135945060608801359350608088013560ff811681146130bd57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b801515811461168457600080fd5b6000602082840312156130fa57600080fd5b8135610aae816130da565b6000806040838503121561311857600080fd5b823561312381612f68565b9150602083013561302c81612f68565b600181811c9082168061314757607f821691505b60208210810361316757634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b6000828210156131bb576131bb613193565b500390565b600082198211156131d3576131d3613193565b500190565b6000602082840312156131ea57600080fd5b8151610aae816130da565b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b60006020828403121561323457600080fd5b5051919050565b60006001820161324d5761324d613193565b5060010190565b600080835481600182811c91508083168061327057607f831692505b6020808410820361328f57634e487b7160e01b86526022600452602486fd5b8180156132a357600181146132b8576132e5565b60ff19861689528415158502890196506132e5565b60008a81526020902060005b868110156132dd5781548b8201529085019083016132c4565b505084890196505b50949897505050505050505056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122030265c565d620ec18d93a75d7fb591d36fcac1ed8706ffd2e0bbb067925b959364736f6c634300080f003300000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000004457df4a5bccf796662b6374d5947c881cc83ac70000000000000000000000004457df4a5bccf796662b6374d5947c881cc83ac7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013524546492050726f2055534443205661756c740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087265666955534443000000000000000000000000000000000000000000000000

Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.

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.