ETH Price: $3,465.66 (-1.17%)
Gas: 5 Gwei

Contract

0xA663B02CF0a4b149d2aD41910CB81e23e1c41c32
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Redeem203760852024-07-24 11:24:2343 mins ago1721820263IN
Frax Finance: sFRAX Token
0 ETH0.000282554.04181643
Redeem203757802024-07-24 10:23:231 hr ago1721816603IN
Frax Finance: sFRAX Token
0 ETH0.000342313.93437837
Redeem203735772024-07-24 3:00:599 hrs ago1721790059IN
Frax Finance: sFRAX Token
0 ETH0.000139812
Approve203734852024-07-24 2:42:359 hrs ago1721788955IN
Frax Finance: sFRAX Token
0 ETH0.000145133.15472277
Redeem203724152024-07-23 23:07:4713 hrs ago1721776067IN
Frax Finance: sFRAX Token
0 ETH0.000305464.37180567
Approve203724132024-07-23 23:07:2313 hrs ago1721776043IN
Frax Finance: sFRAX Token
0 ETH0.00019644.26694698
Redeem203723982024-07-23 23:04:2313 hrs ago1721775863IN
Frax Finance: sFRAX Token
0 ETH0.000388444.46513276
Approve203723062024-07-23 22:45:5913 hrs ago1721774759IN
Frax Finance: sFRAX Token
0 ETH0.00012242.65925684
Redeem203719892024-07-23 21:41:3514 hrs ago1721770895IN
Frax Finance: sFRAX Token
0 ETH0.000486595.59254638
Approve203719832024-07-23 21:40:2314 hrs ago1721770823IN
Frax Finance: sFRAX Token
0 ETH0.000246855.36425389
Redeem203719822024-07-23 21:40:1114 hrs ago1721770811IN
Frax Finance: sFRAX Token
0 ETH0.000456095.24202591
Approve203719792024-07-23 21:39:3514 hrs ago1721770775IN
Frax Finance: sFRAX Token
0 ETH0.000250275.43867261
Deposit203694882024-07-23 13:19:4722 hrs ago1721740787IN
Frax Finance: sFRAX Token
0 ETH0.000433754.64633241
Deposit203684032024-07-23 9:42:1126 hrs ago1721727731IN
Frax Finance: sFRAX Token
0 ETH0.000363895.09267247
Approve203649192024-07-22 22:01:2338 hrs ago1721685683IN
Frax Finance: sFRAX Token
0 ETH0.00024075.22925955
Deposit203642322024-07-22 19:43:3540 hrs ago1721677415IN
Frax Finance: sFRAX Token
0 ETH0.000503315.68370699
Redeem203607582024-07-22 8:04:232 days ago1721635463IN
Frax Finance: sFRAX Token
0 ETH0.000348024
Deposit203605612024-07-22 7:24:352 days ago1721633075IN
Frax Finance: sFRAX Token
0 ETH0.000308973.30886014
Approve203578512024-07-21 22:19:472 days ago1721600387IN
Frax Finance: sFRAX Token
0 ETH0.000375348.10780371
Deposit203578422024-07-21 22:17:592 days ago1721600279IN
Frax Finance: sFRAX Token
0 ETH0.000792438.94865935
Redeem203577332024-07-21 21:55:592 days ago1721598959IN
Frax Finance: sFRAX Token
0 ETH0.000640317.36033418
Redeem203576912024-07-21 21:47:352 days ago1721598455IN
Frax Finance: sFRAX Token
0 ETH0.000783069
Deposit203560042024-07-21 16:08:232 days ago1721578103IN
Frax Finance: sFRAX Token
0 ETH0.000476656.67083908
Approve203559682024-07-21 16:00:592 days ago1721577659IN
Frax Finance: sFRAX Token
0 ETH0.000185174
Redeem203558582024-07-21 15:38:472 days ago1721576327IN
Frax Finance: sFRAX Token
0 ETH0.000412864.74579833
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
183780852023-10-18 15:19:11279 days ago1697642351  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StakedFrax

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 99999999 runs

Other Settings:
london EvmVersion
File 1 of 12 : StakedFrax.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.21;

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ============================ StakedFrax ============================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance

import { Timelock2Step } from "frax-std/access-control/v2/Timelock2Step.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SafeCastLib } from "solmate/utils/SafeCastLib.sol";
import { LinearRewardsErc4626, ERC20 } from "./LinearRewardsErc4626.sol";

/// @title Staked Frax
/// @notice A ERC4626 Vault implementation with linear rewards, rewards can be capped
contract StakedFrax is LinearRewardsErc4626, Timelock2Step {
    using SafeCastLib for *;

    /// @notice The maximum amount of rewards that can be distributed per second per 1e18 asset
    uint256 public maxDistributionPerSecondPerAsset;

    /// @param _underlying The erc20 asset deposited
    /// @param _name The name of the vault
    /// @param _symbol The symbol of the vault
    /// @param _rewardsCycleLength The length of the rewards cycle in seconds
    /// @param _maxDistributionPerSecondPerAsset The maximum amount of rewards that can be distributed per second per 1e18 asset
    /// @param _timelockAddress The address of the timelock/owner contract
    constructor(
        IERC20 _underlying,
        string memory _name,
        string memory _symbol,
        uint32 _rewardsCycleLength,
        uint256 _maxDistributionPerSecondPerAsset,
        address _timelockAddress
    )
        LinearRewardsErc4626(ERC20(address(_underlying)), _name, _symbol, _rewardsCycleLength)
        Timelock2Step(_timelockAddress)
    {
        maxDistributionPerSecondPerAsset = _maxDistributionPerSecondPerAsset;
    }

    /// @notice The ```SetMaxDistributionPerSecondPerAsset``` event is emitted when the maxDistributionPerSecondPerAsset is set
    /// @param oldMax The old maxDistributionPerSecondPerAsset value
    /// @param newMax The new maxDistributionPerSecondPerAsset value
    event SetMaxDistributionPerSecondPerAsset(uint256 oldMax, uint256 newMax);

    /// @notice The ```setMaxDistributionPerSecondPerAsset``` function sets the maxDistributionPerSecondPerAsset
    /// @dev This function can only be called by the timelock, caps the value to type(uint64).max
    /// @param _maxDistributionPerSecondPerAsset The maximum amount of rewards that can be distributed per second per 1e18 asset
    function setMaxDistributionPerSecondPerAsset(uint256 _maxDistributionPerSecondPerAsset) external {
        _requireSenderIsTimelock();
        syncRewardsAndDistribution();

        // NOTE: prevents bricking the contract via overflow
        if (_maxDistributionPerSecondPerAsset > type(uint64).max) {
            _maxDistributionPerSecondPerAsset = type(uint64).max;
        }

        emit SetMaxDistributionPerSecondPerAsset({
            oldMax: maxDistributionPerSecondPerAsset,
            newMax: _maxDistributionPerSecondPerAsset
        });

        maxDistributionPerSecondPerAsset = _maxDistributionPerSecondPerAsset;
    }

    /// @notice The ```calculateRewardsToDistribute``` function calculates the amount of rewards to distribute based on the rewards cycle data and the time passed
    /// @param _rewardsCycleData The rewards cycle data
    /// @param _deltaTime The time passed since the last rewards distribution
    /// @return _rewardToDistribute The amount of rewards to distribute
    function calculateRewardsToDistribute(
        RewardsCycleData memory _rewardsCycleData,
        uint256 _deltaTime
    ) public view override returns (uint256 _rewardToDistribute) {
        _rewardToDistribute = super.calculateRewardsToDistribute({
            _rewardsCycleData: _rewardsCycleData,
            _deltaTime: _deltaTime
        });

        // Cap rewards
        uint256 _maxDistribution = (maxDistributionPerSecondPerAsset * _deltaTime * storedTotalAssets) / PRECISION;
        if (_rewardToDistribute > _maxDistribution) {
            _rewardToDistribute = _maxDistribution;
        }
    }
}

File 2 of 12 : Timelock2Step.sol
// SPDX-License-Identifier: ISC
pragma solidity >=0.8.0;

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================== Timelock2Step ===========================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance

// Primary Author
// Drake Evans: https://github.com/DrakeEvans

// Reviewers
// Dennis: https://github.com/denett

// ====================================================================

/// @title Timelock2Step
/// @author Drake Evans (Frax Finance) https://github.com/drakeevans
/// @dev Inspired by OpenZeppelin's Ownable2Step contract
/// @notice  An abstract contract which contains 2-step transfer and renounce logic for a timelock address
abstract contract Timelock2Step {
    /// @notice The pending timelock address
    address public pendingTimelockAddress;

    /// @notice The current timelock address
    address public timelockAddress;

    constructor(address _timelockAddress) {
        timelockAddress = _timelockAddress;
    }

    // ============================================================================================
    // Functions: External Functions
    // ============================================================================================

    /// @notice The ```transferTimelock``` function initiates the timelock transfer
    /// @dev Must be called by the current timelock
    /// @param _newTimelock The address of the nominated (pending) timelock
    function transferTimelock(address _newTimelock) external virtual {
        _requireSenderIsTimelock();
        _transferTimelock(_newTimelock);
    }

    /// @notice The ```acceptTransferTimelock``` function completes the timelock transfer
    /// @dev Must be called by the pending timelock
    function acceptTransferTimelock() external virtual {
        _requireSenderIsPendingTimelock();
        _acceptTransferTimelock();
    }

    /// @notice The ```renounceTimelock``` function renounces the timelock after setting pending timelock to current timelock
    /// @dev Pending timelock must be set to current timelock before renouncing, creating a 2-step renounce process
    function renounceTimelock() external virtual {
        _requireSenderIsTimelock();
        _requireSenderIsPendingTimelock();
        _transferTimelock(address(0));
        _setTimelock(address(0));
    }

    // ============================================================================================
    // Functions: Internal Actions
    // ============================================================================================

    /// @notice The ```_transferTimelock``` function initiates the timelock transfer
    /// @dev This function is to be implemented by a public function
    /// @param _newTimelock The address of the nominated (pending) timelock
    function _transferTimelock(address _newTimelock) internal {
        pendingTimelockAddress = _newTimelock;
        emit TimelockTransferStarted(timelockAddress, _newTimelock);
    }

    /// @notice The ```_acceptTransferTimelock``` function completes the timelock transfer
    /// @dev This function is to be implemented by a public function
    function _acceptTransferTimelock() internal {
        pendingTimelockAddress = address(0);
        _setTimelock(msg.sender);
    }

    /// @notice The ```_setTimelock``` function sets the timelock address
    /// @dev This function is to be implemented by a public function
    /// @param _newTimelock The address of the new timelock
    function _setTimelock(address _newTimelock) internal {
        emit TimelockTransferred(timelockAddress, _newTimelock);
        timelockAddress = _newTimelock;
    }

    // ============================================================================================
    // Functions: Internal Checks
    // ============================================================================================

    /// @notice The ```_isTimelock``` function checks if _address is current timelock address
    /// @param _address The address to check against the timelock
    /// @return Whether or not msg.sender is current timelock address
    function _isTimelock(address _address) internal view returns (bool) {
        return _address == timelockAddress;
    }

    /// @notice The ```_requireIsTimelock``` function reverts if _address is not current timelock address
    /// @param _address The address to check against the timelock
    function _requireIsTimelock(address _address) internal view {
        if (!_isTimelock(_address)) revert AddressIsNotTimelock(timelockAddress, _address);
    }

    /// @notice The ```_requireSenderIsTimelock``` function reverts if msg.sender is not current timelock address
    /// @dev This function is to be implemented by a public function
    function _requireSenderIsTimelock() internal view {
        _requireIsTimelock(msg.sender);
    }

    /// @notice The ```_isPendingTimelock``` function checks if the _address is pending timelock address
    /// @dev This function is to be implemented by a public function
    /// @param _address The address to check against the pending timelock
    /// @return Whether or not _address is pending timelock address
    function _isPendingTimelock(address _address) internal view returns (bool) {
        return _address == pendingTimelockAddress;
    }

    /// @notice The ```_requireIsPendingTimelock``` function reverts if the _address is not pending timelock address
    /// @dev This function is to be implemented by a public function
    /// @param _address The address to check against the pending timelock
    function _requireIsPendingTimelock(address _address) internal view {
        if (!_isPendingTimelock(_address)) revert AddressIsNotPendingTimelock(pendingTimelockAddress, _address);
    }

    /// @notice The ```_requirePendingTimelock``` function reverts if msg.sender is not pending timelock address
    /// @dev This function is to be implemented by a public function
    function _requireSenderIsPendingTimelock() internal view {
        _requireIsPendingTimelock(msg.sender);
    }

    // ============================================================================================
    // Functions: Events
    // ============================================================================================

    /// @notice The ```TimelockTransferStarted``` event is emitted when the timelock transfer is initiated
    /// @param previousTimelock The address of the previous timelock
    /// @param newTimelock The address of the new timelock
    event TimelockTransferStarted(address indexed previousTimelock, address indexed newTimelock);

    /// @notice The ```TimelockTransferred``` event is emitted when the timelock transfer is completed
    /// @param previousTimelock The address of the previous timelock
    /// @param newTimelock The address of the new timelock
    event TimelockTransferred(address indexed previousTimelock, address indexed newTimelock);

    // ============================================================================================
    // Functions: Errors
    // ============================================================================================

    /// @notice Emitted when timelock is transferred
    error AddressIsNotTimelock(address timelockAddress, address actualAddress);

    /// @notice Emitted when pending timelock is transferred
    error AddressIsNotPendingTimelock(address pendingTimelockAddress, address actualAddress);
}

File 3 of 12 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

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

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

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

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

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

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

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

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

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

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

        return true;
    }

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

        _beforeTokenTransfer(from, to, amount);

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

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// @notice Safe unsigned integer casting library that reverts on overflow.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
    function safeCastTo248(uint256 x) internal pure returns (uint248 y) {
        require(x < 1 << 248);

        y = uint248(x);
    }

    function safeCastTo240(uint256 x) internal pure returns (uint240 y) {
        require(x < 1 << 240);

        y = uint240(x);
    }

    function safeCastTo232(uint256 x) internal pure returns (uint232 y) {
        require(x < 1 << 232);

        y = uint232(x);
    }

    function safeCastTo224(uint256 x) internal pure returns (uint224 y) {
        require(x < 1 << 224);

        y = uint224(x);
    }

    function safeCastTo216(uint256 x) internal pure returns (uint216 y) {
        require(x < 1 << 216);

        y = uint216(x);
    }

    function safeCastTo208(uint256 x) internal pure returns (uint208 y) {
        require(x < 1 << 208);

        y = uint208(x);
    }

    function safeCastTo200(uint256 x) internal pure returns (uint200 y) {
        require(x < 1 << 200);

        y = uint200(x);
    }

    function safeCastTo192(uint256 x) internal pure returns (uint192 y) {
        require(x < 1 << 192);

        y = uint192(x);
    }

    function safeCastTo184(uint256 x) internal pure returns (uint184 y) {
        require(x < 1 << 184);

        y = uint184(x);
    }

    function safeCastTo176(uint256 x) internal pure returns (uint176 y) {
        require(x < 1 << 176);

        y = uint176(x);
    }

    function safeCastTo168(uint256 x) internal pure returns (uint168 y) {
        require(x < 1 << 168);

        y = uint168(x);
    }

    function safeCastTo160(uint256 x) internal pure returns (uint160 y) {
        require(x < 1 << 160);

        y = uint160(x);
    }

    function safeCastTo152(uint256 x) internal pure returns (uint152 y) {
        require(x < 1 << 152);

        y = uint152(x);
    }

    function safeCastTo144(uint256 x) internal pure returns (uint144 y) {
        require(x < 1 << 144);

        y = uint144(x);
    }

    function safeCastTo136(uint256 x) internal pure returns (uint136 y) {
        require(x < 1 << 136);

        y = uint136(x);
    }

    function safeCastTo128(uint256 x) internal pure returns (uint128 y) {
        require(x < 1 << 128);

        y = uint128(x);
    }

    function safeCastTo120(uint256 x) internal pure returns (uint120 y) {
        require(x < 1 << 120);

        y = uint120(x);
    }

    function safeCastTo112(uint256 x) internal pure returns (uint112 y) {
        require(x < 1 << 112);

        y = uint112(x);
    }

    function safeCastTo104(uint256 x) internal pure returns (uint104 y) {
        require(x < 1 << 104);

        y = uint104(x);
    }

    function safeCastTo96(uint256 x) internal pure returns (uint96 y) {
        require(x < 1 << 96);

        y = uint96(x);
    }

    function safeCastTo88(uint256 x) internal pure returns (uint88 y) {
        require(x < 1 << 88);

        y = uint88(x);
    }

    function safeCastTo80(uint256 x) internal pure returns (uint80 y) {
        require(x < 1 << 80);

        y = uint80(x);
    }

    function safeCastTo72(uint256 x) internal pure returns (uint72 y) {
        require(x < 1 << 72);

        y = uint72(x);
    }

    function safeCastTo64(uint256 x) internal pure returns (uint64 y) {
        require(x < 1 << 64);

        y = uint64(x);
    }

    function safeCastTo56(uint256 x) internal pure returns (uint56 y) {
        require(x < 1 << 56);

        y = uint56(x);
    }

    function safeCastTo48(uint256 x) internal pure returns (uint48 y) {
        require(x < 1 << 48);

        y = uint48(x);
    }

    function safeCastTo40(uint256 x) internal pure returns (uint40 y) {
        require(x < 1 << 40);

        y = uint40(x);
    }

    function safeCastTo32(uint256 x) internal pure returns (uint32 y) {
        require(x < 1 << 32);

        y = uint32(x);
    }

    function safeCastTo24(uint256 x) internal pure returns (uint24 y) {
        require(x < 1 << 24);

        y = uint24(x);
    }

    function safeCastTo16(uint256 x) internal pure returns (uint16 y) {
        require(x < 1 << 16);

        y = uint16(x);
    }

    function safeCastTo8(uint256 x) internal pure returns (uint8 y) {
        require(x < 1 << 8);

        y = uint8(x);
    }
}

File 5 of 12 : LinearRewardsErc4626.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.21;

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ======================== LinearRewardsErc4626 ======================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance

import { ERC20, ERC4626 } from "solmate/mixins/ERC4626.sol";
import { SafeCastLib } from "solmate/utils/SafeCastLib.sol";

/// @title LinearRewardsErc4626
/// @notice An ERC4626 Vault implementation with linear rewards
abstract contract LinearRewardsErc4626 is ERC4626 {
    using SafeCastLib for *;

    /// @notice The precision of all integer calculations
    uint256 public constant PRECISION = 1e18;

    /// @notice The rewards cycle length in seconds
    uint256 public immutable REWARDS_CYCLE_LENGTH;

    /// @notice Information about the current rewards cycle
    struct RewardsCycleData {
        uint40 cycleEnd; // Timestamp of the end of the current rewards cycle
        uint40 lastSync; // Timestamp of the last time the rewards cycle was synced
        uint216 rewardCycleAmount; // Amount of rewards to be distributed in the current cycle
    }

    /// @notice The rewards cycle data, stored in a single word to save gas
    RewardsCycleData public rewardsCycleData;

    /// @notice The timestamp of the last time rewards were distributed
    uint256 public lastRewardsDistribution;

    /// @notice The total amount of assets that have been distributed and deposited
    uint256 public storedTotalAssets;

    /// @notice The precision of the underlying asset
    uint256 public immutable UNDERLYING_PRECISION;

    /// @param _underlying The erc20 asset deposited
    /// @param _name The name of the vault
    /// @param _symbol The symbol of the vault
    /// @param _rewardsCycleLength The length of the rewards cycle in seconds
    constructor(
        ERC20 _underlying,
        string memory _name,
        string memory _symbol,
        uint256 _rewardsCycleLength
    ) ERC4626(_underlying, _name, _symbol) {
        REWARDS_CYCLE_LENGTH = _rewardsCycleLength;
        UNDERLYING_PRECISION = 10 ** _underlying.decimals();

        // initialize rewardsCycleEnd value
        // NOTE: normally distribution of rewards should be done prior to _syncRewards but in this case we know there are no users or rewards yet.
        _syncRewards();

        // initialize lastRewardsDistribution value
        _distributeRewards();
    }

    function pricePerShare() external view returns (uint256 _pricePerShare) {
        _pricePerShare = convertToAssets(UNDERLYING_PRECISION);
    }

    /// @notice The ```calculateRewardsToDistribute``` function calculates the amount of rewards to distribute based on the rewards cycle data and the time elapsed
    /// @param _rewardsCycleData The rewards cycle data
    /// @param _deltaTime The time elapsed since the last rewards distribution
    /// @return _rewardToDistribute The amount of rewards to distribute
    function calculateRewardsToDistribute(
        RewardsCycleData memory _rewardsCycleData,
        uint256 _deltaTime
    ) public view virtual returns (uint256 _rewardToDistribute) {
        _rewardToDistribute =
            (_rewardsCycleData.rewardCycleAmount * _deltaTime) /
            (_rewardsCycleData.cycleEnd - _rewardsCycleData.lastSync);
    }

    /// @notice The ```previewDistributeRewards``` function is used to preview the rewards distributed at the top of the block
    /// @return _rewardToDistribute The amount of underlying to distribute
    function previewDistributeRewards() public view virtual returns (uint256 _rewardToDistribute) {
        // Cache state for gas savings
        RewardsCycleData memory _rewardsCycleData = rewardsCycleData;
        uint256 _lastRewardsDistribution = lastRewardsDistribution;
        uint40 _timestamp = block.timestamp.safeCastTo40();

        // Calculate the delta time, but only include up to the cycle end in case we are passed it
        uint256 _deltaTime = _timestamp > _rewardsCycleData.cycleEnd
            ? _rewardsCycleData.cycleEnd - _lastRewardsDistribution
            : _timestamp - _lastRewardsDistribution;

        // Calculate the rewards to distribute
        _rewardToDistribute = calculateRewardsToDistribute({
            _rewardsCycleData: _rewardsCycleData,
            _deltaTime: _deltaTime
        });
    }

    /// @notice The ```distributeRewards``` function distributes the rewards once per block
    /// @return _rewardToDistribute The amount of underlying to distribute
    function _distributeRewards() internal virtual returns (uint256 _rewardToDistribute) {
        _rewardToDistribute = previewDistributeRewards();

        // Only write to state/emit if we actually distribute rewards
        if (_rewardToDistribute != 0) {
            storedTotalAssets += _rewardToDistribute;
            emit DistributeRewards({ rewardsToDistribute: _rewardToDistribute });
        }

        lastRewardsDistribution = block.timestamp;
    }

    /// @notice The ```previewSyncRewards``` function returns the updated rewards cycle data without updating the state
    /// @return _newRewardsCycleData The updated rewards cycle data
    function previewSyncRewards() public view virtual returns (RewardsCycleData memory _newRewardsCycleData) {
        RewardsCycleData memory _rewardsCycleData = rewardsCycleData;

        uint256 _timestamp = block.timestamp;

        // Only sync if the previous cycle has ended
        if (_timestamp <= _rewardsCycleData.cycleEnd) return _rewardsCycleData;

        // Calculate rewards for next cycle
        uint256 _newRewards = asset.balanceOf(address(this)) - storedTotalAssets;

        // Calculate the next cycle end, this keeps cycles at the same time regardless of when sync is called
        uint40 _cycleEnd = (((_timestamp + REWARDS_CYCLE_LENGTH) / REWARDS_CYCLE_LENGTH) * REWARDS_CYCLE_LENGTH)
            .safeCastTo40();

        // This block prevents big jumps in rewards rate in case the sync happens near the end of the cycle
        if (_cycleEnd - _timestamp < REWARDS_CYCLE_LENGTH / 40) {
            _cycleEnd += REWARDS_CYCLE_LENGTH.safeCastTo40();
        }

        // Write return values
        _rewardsCycleData.rewardCycleAmount = _newRewards.safeCastTo216();
        _rewardsCycleData.lastSync = _timestamp.safeCastTo40();
        _rewardsCycleData.cycleEnd = _cycleEnd;

        return _rewardsCycleData;
    }

    /// @notice The ```_syncRewards``` function is used to update the rewards cycle data
    function _syncRewards() internal virtual {
        RewardsCycleData memory _rewardsCycleData = previewSyncRewards();

        if (
            block
                .timestamp
                // If true, then preview shows a rewards should be processed
                .safeCastTo40() ==
            _rewardsCycleData.lastSync &&
            // Ensures that we don't write to state twice in the same block
            rewardsCycleData.lastSync != _rewardsCycleData.lastSync
        ) {
            rewardsCycleData = _rewardsCycleData;
            emit SyncRewards({
                cycleEnd: _rewardsCycleData.cycleEnd,
                lastSync: _rewardsCycleData.lastSync,
                rewardCycleAmount: _rewardsCycleData.rewardCycleAmount
            });
        }
    }

    /// @notice The ```syncRewardsAndDistribution``` function is used to update the rewards cycle data and distribute rewards
    /// @dev rewards must be distributed before the cycle is synced
    function syncRewardsAndDistribution() public virtual {
        _distributeRewards();
        _syncRewards();
    }

    /// @notice The ```totalAssets``` function returns the total assets available in the vault
    /// @dev This function simulates the rewards that will be distributed at the top of the block
    /// @return _totalAssets The total assets available in the vault
    function totalAssets() public view virtual override returns (uint256 _totalAssets) {
        uint256 _rewardToDistribute = previewDistributeRewards();
        _totalAssets = storedTotalAssets + _rewardToDistribute;
    }

    function afterDeposit(uint256 amount, uint256 shares) internal virtual override {
        storedTotalAssets += amount;
    }

    /// @notice The ```deposit``` function allows a user to mint shares by depositing underlying
    /// @param _assets The amount of underlying to deposit
    /// @param _receiver The address to send the shares to
    /// @return _shares The amount of shares minted
    function deposit(uint256 _assets, address _receiver) public override returns (uint256 _shares) {
        syncRewardsAndDistribution();
        _shares = super.deposit({ assets: _assets, receiver: _receiver });
    }

    /// @notice The ```mint``` function allows a user to mint a given number of shares
    /// @param _shares The amount of shares to mint
    /// @param _receiver The address to send the shares to
    /// @return _assets The amount of underlying deposited
    function mint(uint256 _shares, address _receiver) public override returns (uint256 _assets) {
        syncRewardsAndDistribution();
        _assets = super.mint({ shares: _shares, receiver: _receiver });
    }

    function beforeWithdraw(uint256 amount, uint256 shares) internal virtual override {
        storedTotalAssets -= amount;
    }

    /// @notice The ```withdraw``` function allows a user to withdraw a given amount of underlying
    /// @param _assets The amount of underlying to withdraw
    /// @param _receiver The address to send the underlying to
    /// @param _owner The address of the owner of the shares
    /// @return _shares The amount of shares burned
    function withdraw(uint256 _assets, address _receiver, address _owner) public override returns (uint256 _shares) {
        syncRewardsAndDistribution();

        _shares = super.withdraw({ assets: _assets, receiver: _receiver, owner: _owner });
    }

    /// @notice The ```redeem``` function allows a user to redeem their shares for underlying
    /// @param _shares The amount of shares to redeem
    /// @param _receiver The address to send the underlying to
    /// @param _owner The address of the owner of the shares
    /// @return _assets The amount of underlying redeemed
    function redeem(uint256 _shares, address _receiver, address _owner) public override returns (uint256 _assets) {
        syncRewardsAndDistribution();

        _assets = super.redeem({ shares: _shares, receiver: _receiver, owner: _owner });
    }

    /// @notice The ```depositWithSignature``` function allows a user to use signed approvals to deposit
    /// @param _assets The amount of underlying to deposit
    /// @param _receiver The address to send the shares to
    /// @param _deadline The deadline for the signature
    /// @param _approveMax Whether or not to approve the maximum amount
    /// @param _v The v value of the signature
    /// @param _r The r value of the signature
    /// @param _s The s value of the signature
    /// @return _shares The amount of shares minted
    function depositWithSignature(
        uint256 _assets,
        address _receiver,
        uint256 _deadline,
        bool _approveMax,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    ) external returns (uint256 _shares) {
        uint256 _amount = _approveMax ? type(uint256).max : _assets;
        asset.permit({
            owner: msg.sender,
            spender: address(this),
            value: _amount,
            deadline: _deadline,
            v: _v,
            r: _r,
            s: _s
        });
        _shares = (deposit({ _assets: _assets, _receiver: _receiver }));
    }

    //==============================================================================
    // Events
    //==============================================================================

    /// @notice The ```SyncRewards``` event is emitted when the rewards cycle is synced
    /// @param cycleEnd The timestamp of the end of the current rewards cycle
    /// @param lastSync The timestamp of the last time the rewards cycle was synced
    /// @param rewardCycleAmount The amount of rewards to be distributed in the current cycle
    event SyncRewards(uint40 cycleEnd, uint40 lastSync, uint216 rewardCycleAmount);

    /// @notice The ```DistributeRewards``` event is emitted when rewards are distributed to storedTotalAssets
    /// @param rewardsToDistribute The amount of rewards that were distributed
    event DistributeRewards(uint256 rewardsToDistribute);
}

File 6 of 12 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

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

pragma solidity ^0.8.0;

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

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

File 9 of 12 : ERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)
abstract contract ERC4626 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
    //////////////////////////////////////////////////////////////*/

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual returns (uint256 shares) {
        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

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

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual returns (uint256 assets) {
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

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

        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    /*//////////////////////////////////////////////////////////////
                            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);
    }

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

    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return convertToAssets(balanceOf[owner]);
    }

    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HOOKS LOGIC
    //////////////////////////////////////////////////////////////*/

    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}

    function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}

File 10 of 12 : 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/transmissions11/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 11 of 12 : 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/transmissions11/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;

        /// @solidity memory-safe-assembly
        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;

        /// @solidity memory-safe-assembly
        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), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            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;

        /// @solidity memory-safe-assembly
        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), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            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;

        /// @solidity memory-safe-assembly
        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), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            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 12 of 12 : 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/transmissions11/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 MAX_UINT256 = 2**256 - 1;

    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) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        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) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            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)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

Settings
{
  "remappings": [
    "frax-std/=lib/frax-standard-solidity/src/",
    "ds-test/=lib/frax-standard-solidity/lib/forge-std/lib/ds-test/src/",
    "solmate/=lib/solmate/src/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "forge-std/=lib/frax-standard-solidity/lib/forge-std/src/",
    "frax-standard-solidity/=lib/frax-standard-solidity/src/",
    "solidity-bytes-utils/=lib/frax-standard-solidity/lib/solidity-bytes-utils/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 99999999
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC20","name":"_underlying","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint32","name":"_rewardsCycleLength","type":"uint32"},{"internalType":"uint256","name":"_maxDistributionPerSecondPerAsset","type":"uint256"},{"internalType":"address","name":"_timelockAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"pendingTimelockAddress","type":"address"},{"internalType":"address","name":"actualAddress","type":"address"}],"name":"AddressIsNotPendingTimelock","type":"error"},{"inputs":[{"internalType":"address","name":"timelockAddress","type":"address"},{"internalType":"address","name":"actualAddress","type":"address"}],"name":"AddressIsNotTimelock","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":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":"rewardsToDistribute","type":"uint256"}],"name":"DistributeRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMax","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMax","type":"uint256"}],"name":"SetMaxDistributionPerSecondPerAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint40","name":"cycleEnd","type":"uint40"},{"indexed":false,"internalType":"uint40","name":"lastSync","type":"uint40"},{"indexed":false,"internalType":"uint216","name":"rewardCycleAmount","type":"uint216"}],"name":"SyncRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousTimelock","type":"address"},{"indexed":true,"internalType":"address","name":"newTimelock","type":"address"}],"name":"TimelockTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousTimelock","type":"address"},{"indexed":true,"internalType":"address","name":"newTimelock","type":"address"}],"name":"TimelockTransferred","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":"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":[],"name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDS_CYCLE_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNDERLYING_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptTransferTimelock","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint40","name":"cycleEnd","type":"uint40"},{"internalType":"uint40","name":"lastSync","type":"uint40"},{"internalType":"uint216","name":"rewardCycleAmount","type":"uint216"}],"internalType":"struct LinearRewardsErc4626.RewardsCycleData","name":"_rewardsCycleData","type":"tuple"},{"internalType":"uint256","name":"_deltaTime","type":"uint256"}],"name":"calculateRewardsToDistribute","outputs":[{"internalType":"uint256","name":"_rewardToDistribute","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":"_receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"bool","name":"_approveMax","type":"bool"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"depositWithSignature","outputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastRewardsDistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxDistributionPerSecondPerAsset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"stateMutability":"nonpayable","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":"pendingTimelockAddress","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":[],"name":"previewDistributeRewards","outputs":[{"internalType":"uint256","name":"_rewardToDistribute","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":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"previewSyncRewards","outputs":[{"components":[{"internalType":"uint40","name":"cycleEnd","type":"uint40"},{"internalType":"uint40","name":"lastSync","type":"uint40"},{"internalType":"uint216","name":"rewardCycleAmount","type":"uint216"}],"internalType":"struct LinearRewardsErc4626.RewardsCycleData","name":"_newRewardsCycleData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerShare","outputs":[{"internalType":"uint256","name":"_pricePerShare","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardsCycleData","outputs":[{"internalType":"uint40","name":"cycleEnd","type":"uint40"},{"internalType":"uint40","name":"lastSync","type":"uint40"},{"internalType":"uint216","name":"rewardCycleAmount","type":"uint216"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxDistributionPerSecondPerAsset","type":"uint256"}],"name":"setMaxDistributionPerSecondPerAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storedTotalAssets","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":"syncRewardsAndDistribution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"timelockAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"_totalAssets","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":"address","name":"_newTimelock","type":"address"}],"name":"transferTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

61014080604052346200083c576200355b80380380916200002182856200085d565b833981019060c0818303126200083c578051906001600160a01b0380831683036200083c5760208201516001600160401b0381116200083c57846200006891840162000881565b604083015190946001600160401b0382116200083c576200008b91840162000881565b9260608301519463ffffffff86168096036200083c5760a060808501519401519483861686036200083c5760405163313ce56760e01b8152916020836004818789165afa9283156200059e5760009362000804575b508051906001600160401b038211620005aa5781906200010260005462000912565b601f8111620007a1575b50602090601f8311600114620007255760009262000719575b50508160011b916000199060031b1c1916176000555b8051906001600160401b038211620005aa5781906200015c60015462000912565b601f8111620006b5575b50602090601f83116001146200063b576000926200062f575b50508160011b916000199060031b1c1916176001555b6080524660a05260405160009081549181620001b18462000912565b918282526020820194600181169081600014620006115750600114620005c0575b620001e0925003826200085d565b519020906040519160208301907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f825260408401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608401524660808401523060a084015260a0835260c083019183831060018060401b03841117620005aa57600483868193602095836040528851902060c05281811660e0526101009b8c5263313ce56760e01b8352165afa9081156200059e5760ff9260009262000562575b50501692604d84116200054c5761012093600a0a8452620002c3620009f5565b620002ce42620009a7565b60208201519164ffffffffff9283928382169384911614806200053a575b620004b3575b505050620002ff6200095d565b6008546200030d42620009a7565b825190841691908416821115620004a1576200032e91508383511662000999565b915b60408201516000906200034e9085906001600160d81b0316620009e1565b9282602081835116920151169003908282116200048d575091620003799162000383931690620009c0565b91600c54620009e1565b670de0b6b3a76400006200039b6009548093620009e1565b0480831162000484575b508162000442575b5050426008551660018060a01b0319600b541617600b55600c556040519061298c928362000b8f8439608051836116a4015260a05183611ea2015260c05183611ec9015260e051838181610871015281816109fa01528181610de701528181610fc80152818161152d0152818161227401526125bd0152518281816111f901526122b3015251818181610be40152610c3f0152f35b81620004727fb9d196a585c1a894f648393ec7d52cc59ff6d94191579d073ba32b0a74d7f7a6936020936200094f565b600955604051908152a13880620003ad565b915038620003a5565b634e487b7160e01b81526011600452602490fd5b620004ac9162000999565b9162000330565b7fc32a546ed958490e37f30335e501e0a39438cb650a4851bfd4b775490af29dad9282856060945116928369ffffffffff00000000006006549260281b169160018060501b0319161717600655604060018060d81b0391015116908164ffffffffff60d81b600754161760075560405192835260208301526040820152a1388080620002f2565b50828460065460281c161415620002ec565b634e487b7160e01b600052601160045260246000fd5b6200058d925060c0906020903d60201162000595575b6200058482856200085d565b010190620008f7565b3880620002a3565b3d915062000578565b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b50600080805290916000805160206200351b8339815191525b818310620005f4575050906020620001e092820101620001d2565b6020919350806001915483858801015201910190918392620005d9565b60ff1916865250620001e092151560051b82016020019050620001d2565b0151905038806200017f565b600160009081526000805160206200353b8339815191529350601f198516905b8181106200069c575090846001959493921062000682575b505050811b0160015562000195565b015160001960f88460031b161c1916905538808062000673565b929360206001819287860151815501950193016200065b565b60016000529091506000805160206200353b833981519152601f840160051c810191602085106200070e575b90601f859493920160051c01905b818110620006fe575062000166565b60008155849350600101620006ef565b9091508190620006e1565b01519050388062000125565b600080805293506000805160206200351b83398151915291905b601f198416851062000785576001945083601f198116106200076b575b505050811b016000556200013b565b015160001960f88460031b161c191690553880806200075c565b818101518355602094850194600190930192909101906200073f565b600080529091506000805160206200351b833981519152601f840160051c81019160208510620007f9575b90601f859493920160051c01905b818110620007e957506200010c565b60008155849350600101620007da565b9091508190620007cc565b6200082c91935060203d60201162000834575b6200082381836200085d565b810190620008f7565b9138620000e0565b503d62000817565b600080fd5b606081019081106001600160401b03821117620005aa57604052565b601f909101601f19168101906001600160401b03821190821017620005aa57604052565b919080601f840112156200083c5782516001600160401b038111620005aa5760209060405192620008bc83601f19601f85011601856200085d565b8184528282870101116200083c5760005b818110620008e357508260009394955001015290565b8581018301518482018401528201620008cd565b908160209103126200083c575160ff811681036200083c5790565b90600182811c9216801562000944575b60208310146200092e57565b634e487b7160e01b600052602260045260246000fd5b91607f169162000922565b919082018092116200054c57565b604051906200096c8262000841565b60065464ffffffffff808216845260289190911c1660208301526007546001600160d81b03166040830152565b919082039182116200054c57565b650100000000008110156200083c5764ffffffffff1690565b8115620009cb570490565b634e487b7160e01b600052601260045260246000fd5b818102929181159184041417156200054c57565b60405162000a038162000841565b6000908181528160406020928284820152015262000a206200095d565b9164ffffffffff918284511642111562000b885760e0516040516370a0823160e01b8152306004820152908290829060249082906001600160a01b03165afa801562000b7d57839062000b43575b62000a7e91506009549062000999565b6101009262000ab362000aad62000aa462000a9b8751426200094f565b875190620009c0565b865190620009e1565b620009a7565b93858086169162000ac5428462000999565b905190602882041162000b0c575b505050600160d81b82101562000b0957506001600160d81b031660408501528262000afe42620009a7565b169084015216815290565b80fd5b62000b1b9192939650620009a7565b160184811162000b2f579238858162000ad3565b634e487b7160e01b84526011600452602484fd5b508181813d831162000b75575b62000b5c81836200085d565b8101031262000b715762000a7e905162000a6e565b8280fd5b503d62000b50565b6040513d85823e3d90fd5b5050509056fe6040608081526004908136101561001557600080fd5b600091823560e01c91826301e1d11414611a4c57826306fdde031461198657826307a2d13a146112f0578263090f3f5014611933578263095ea7b3146118985782630a28a4771461185b57826318160ddd1461181e57826323b872dd146117055782632af98d6d146116c8578263313ce5671461166c578263358245fc146116315782633644e515146115f6578263374010a41461155157826338d52e0f146114e2578263402d267d14610787578263450140951461143457826348f76e0f146113805782634bc66f321461132d5782634cdad506146112f05782634f8b4ae71461121c57826356caf605146111c35782635ebae5661461114957826361c1c5e91461110c5782636e553f65146110c757826370a082311461039057826375e077c314610f395782637ecebe0014610ed75782638f765d5914610e5b57826394bf804d14610d7957826395d89b4114610c6357826399530b0614610c07578263a80e754714610bae578263a9059cbb14610b01578263aaf5eb6814610ac0578263b3d7f6b914610a83578263b460af9414610963578263ba087652146107c9578263bd6f36031461078c578263c63d75b614610787578263c6e6f592146102d4578263ce96cb7714610724578263d505accf14610430578263d71356e6146103f6578263d905777e14610390578263dd62ed3e14610318578263ef8b30f7146102d457505063f6ccaad41461022957600080fd5b346102d157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15761025f611d66565b7fffffffffffffffffffffffff000000000000000000000000000000000000000080600a5416600a55600b54903373ffffffffffffffffffffffffffffffffffffffff83167f31b6c5a04b069b6ec1b3cef44c4e7c1eadd721349cda9823d0b1877b3551cdc68580a3163317600b5580f35b80fd5b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1575061031160209235611dd8565b9051908152f35b91503461038c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261038c576020928291610355611bd7565b61035d611bff565b9173ffffffffffffffffffffffffffffffffffffffff8092168452865283832091168252845220549051908152f35b8280fd5b8390346103f25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2578060209273ffffffffffffffffffffffffffffffffffffffff6103e2611bd7565b1681526003845220549051908152f35b5080fd5b83346102d157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15761042d6123fc565b80f35b8390346103f25760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257610469611bd7565b90610472611bff565b9160443560643592610482611c84565b924285106106c757610492611e9d565b9573ffffffffffffffffffffffffffffffffffffffff8092169586895260209560058752848a209889549960018b01905585519285898501957f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c987528b89870152169a8b606086015288608086015260a085015260c084015260c0835260e0830167ffffffffffffffff948482108683111761069a57818852845190206101008501927f19010000000000000000000000000000000000000000000000000000000000008452610102860152610122850152604281526101608401948186109086111761066e57848752519020835260ff1661018082015260a4356101a082015260c4356101c0909101528780528490889060809060015afa1561066457865116968715158061065b575b156106005786977f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259596975283528087208688528352818188205551908152a380f35b8360649251917f08c379a0000000000000000000000000000000000000000000000000000000008352820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152fd5b508488146105bd565b81513d88823e3d90fd5b60248c60418f7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5060248c60418f7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b60648860208451917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152fd5b8390346103f25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576103118160209373ffffffffffffffffffffffffffffffffffffffff610779611bd7565b168152600385522054611dfd565b611c22565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020906008549051908152f35b9083346102d1576107d936611c94565b6107e5939291936123fc565b73ffffffffffffffffffffffffffffffffffffffff90818116938433036108f8575b5061081183611dfd565b95861561089b57509282602097959261083d88956108959761083588600954611e61565b6009556127d7565b865191858352898301528316907ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db873392a47f000000000000000000000000000000000000000000000000000000000000000061283e565b51908152f35b60649060208951917f08c379a0000000000000000000000000000000000000000000000000000000008352820152600b60248201527f5a45524f5f4153534554530000000000000000000000000000000000000000006044820152fd5b848152866020528781203382526020528780822054857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361093e575b505050610807565b61094791611e61565b9186815288602052818120338252602052205587878185610936565b8390346103f25761089560209361097936611c94565b959091926109856123fc565b6109c561099185611e3f565b809873ffffffffffffffffffffffffffffffffffffffff938985831696873303610a1e575b50505061083587600954611e61565b85519084825287898301528316907ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db873392a47f000000000000000000000000000000000000000000000000000000000000000061283e565b878152828e528181203382528e5281812054857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610a60575b50506109b6565b610a6991611e61565b928882528e528181203382528e5220558b89818085610a59565b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1575061031160209235611e1e565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f25760209051670de0b6b3a76400008152f35b8390346103f257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602091610b3c611bd7565b8273ffffffffffffffffffffffffffffffffffffffff6024359233855260038752828520610b6b858254611e61565b90551692838152600386522081815401905582519081527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef843392a35160018152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020906103117f0000000000000000000000000000000000000000000000000000000000000000611dfd565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f25780519082600190815491610ca683611a92565b918286526020938281169081600014610d395750600114610ce1575b610cdd8686610cd3828b0383611b30565b5191829182611b71565b0390f35b9295508083527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b828410610d265750505082610cdd94610cd3928201019486610cc2565b8054868501880152928601928101610d09565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001687860152505050151560051b8301019250610cd382610cdd86610cc2565b91503461038c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261038c57602092503590610db7611bff565b91610dc06123fc565b73ffffffffffffffffffffffffffffffffffffffff610dde82611e1e565b93610e0b8530337f0000000000000000000000000000000000000000000000000000000000000000612707565b610e1583826126a1565b8351928584528684015216907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7833392a3610e5282600954612199565b60095551908152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257606090610e966121f2565b907affffffffffffffffffffffffffffffffffffffffffffffffffffff8180519364ffffffffff808251168652602082015116602086015201511690820152f35b8390346103f25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2578060209273ffffffffffffffffffffffffffffffffffffffff610f29611bd7565b1681526005845220549051908152f35b83346102d15760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1578235610f73611bff565b916064359081151582036102d157610f89611c84565b91156110c1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b1561038c579060e48884809460ff8b5198899687957fd505accf00000000000000000000000000000000000000000000000000000000875233908701523060248701526044860152604435606486015216608484015260a43560a484015260c43560c48401525af180156110b55761106a575b6020856103118686612598565b67ffffffffffffffff821161108957508352602093506103118561105d565b806041877f4e487b71000000000000000000000000000000000000000000000000000000006024945252fd5b508451903d90823e3d90fd5b82610fb1565b83346102d157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15750610311602092611105611bff565b9035612598565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020906009549051908152f35b83346102d157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15750600654600754915164ffffffffff808316825260289290921c90911660208201527affffffffffffffffffffffffffffffffffffffffffffffffffffff9091166040820152606090f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b83346102d157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d157611253611cf4565b61125b611d66565b7fffffffffffffffffffffffff000000000000000000000000000000000000000080600a5416600a55600b548273ffffffffffffffffffffffffffffffffffffffff821681817f162998b90abc2507f3953aa797827b03a14c42dbd9a35f09feaf02e0d592773a8280a37f31b6c5a04b069b6ec1b3cef44c4e7c1eadd721349cda9823d0b1877b3551cdc68280a316600b5580f35b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1575061031160209235611dfd565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f25760209073ffffffffffffffffffffffffffffffffffffffff600b54169051908152f35b91503461038c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261038c5735906113bb611cf4565b6113c36123fc565b67ffffffffffffffff80831161140c575b507f05d530f0fd6974b7d995fd3b71870f5301bb9fe086180bdd0bd36526728f5c6b90600c548151908152836020820152a1600c5580f35b91507f05d530f0fd6974b7d995fd3b71870f5301bb9fe086180bdd0bd36526728f5c6b6113d4565b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15761146c611bd7565b611474611cf4565b73ffffffffffffffffffffffffffffffffffffffff80911690817fffffffffffffffffffffffff0000000000000000000000000000000000000000600a541617600a55600b54167f162998b90abc2507f3953aa797827b03a14c42dbd9a35f09feaf02e0d592773a8380a380f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b83346102d1577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601608081126103f2576060136102d15781519261159584611ae5565b3564ffffffffff90818116810361038c57845260243590811681036103f2576020840152604435907affffffffffffffffffffffffffffffffffffffffffffffffffffff821682036102d15750826103119183602095015260643590612905565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090610311611e9d565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090610311612137565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020905160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090600c549051908152f35b83346102d15760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15761173d611bd7565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef611766611bff565b946044358573ffffffffffffffffffffffffffffffffffffffff80951694858752602098848a958652838920338a52865283892054857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036117fb575b505050868852600385528288206117dc858254611e61565b9055169586815260038452208181540190558551908152a35160018152f35b61180491611e61565b90888a528652838920338a528652838920558a80856117c4565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020906002549051908152f35b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1575061031160209235611e3f565b91503461038c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261038c576020926118d3611bd7565b9183602435928392338252875273ffffffffffffffffffffffffffffffffffffffff8282209516948582528752205582519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925843392a35160018152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f25760209073ffffffffffffffffffffffffffffffffffffffff600a54169051908152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2578051908280546119c581611a92565b908185526020926001918281169081600014610d3957506001146119f457610cdd8686610cd3828b0383611b30565b8080949750527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b828410611a395750505082610cdd94610cd3928201019486610cc2565b8054868501880152928601928101611a1c565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090610311611a8a612137565b600954612199565b90600182811c92168015611adb575b6020831014611aac57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691611aa1565b6060810190811067ffffffffffffffff821117611b0157604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611b0157604052565b60208082528251818301819052939260005b858110611bc3575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201611b83565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203611bfa57565b600080fd5b6024359073ffffffffffffffffffffffffffffffffffffffff82168203611bfa57565b34611bfa5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112611bfa57611c59611bd7565b5060206040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8152f35b6084359060ff82168203611bfa57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6060910112611bfa576004359073ffffffffffffffffffffffffffffffffffffffff906024358281168103611bfa57916044359081168103611bfa5790565b73ffffffffffffffffffffffffffffffffffffffff600b5416803303611d175750565b6040517f443dc2b400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b73ffffffffffffffffffffffffffffffffffffffff600a5416803303611d895750565b6040517fbe5a953700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b60025480611de4575090565b90611dfa91611df4611a8a612137565b9161207a565b90565b60025480611e09575090565b611dfa91611e18611a8a612137565b9061207a565b60025480611e2a575090565b611dfa91611e39611a8a612137565b906120ae565b60025480611e4b575090565b90611dfa91611e5b611a8a612137565b916120ae565b91908203918211611e6e57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000467f000000000000000000000000000000000000000000000000000000000000000003611eeb57507f000000000000000000000000000000000000000000000000000000000000000090565b60405181548291611efb82611a92565b808252816020948582019460019087828216918260001461203e575050600114611fe5575b50611f2d92500382611b30565b51902091604051918201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f845260408301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a083015260a0825260c082019082821067ffffffffffffffff831117611fb8575060405251902090565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526041600452fd5b87805286915087907f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b858310612026575050611f2d935082010138611f20565b8054838801850152869450889390920191810161200f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168852611f2d95151560051b8501019250389150611f209050565b817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048111820215830215611bfa57020490565b817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048111820215830215611bfa570290808204910615150190565b604051906120f782611ae5565b8160065464ffffffffff90818116835260281c16602082015260407affffffffffffffffffffffffffffffffffffffffffffffffffffff60075416910152565b611dfa6121426120ea565b60085461214e42612181565b825164ffffffffff92918316908316811115612177575061217191835116611e61565b90612905565b6121719250611e61565b65010000000000811015611bfa5764ffffffffff1690565b91908201809211611e6e57565b81156121b0570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b81810292918115918404141715611e6e57565b6040516121fe81611ae5565b600090818152816040602092828482015201526122196120ea565b9164ffffffffff91828451164211156123f6576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152818160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa80156123eb5783906123bc575b6122b1915060095490611e61565b7f0000000000000000000000000000000000000000000000000000000000000000926122f76122f2856122ed816122e88142612199565b6121a6565b6121df565b612181565b9385808616916123074284611e61565b6028820411612370575b5050507b010000000000000000000000000000000000000000000000000000008210156102d157507affffffffffffffffffffffffffffffffffffffffffffffffffffff1660408501528261236542612181565b169084015216815290565b61237d9192939650612181565b160184811161238f5792388581612311565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b508181813d83116123e4575b6123d28183611b30565b8101031261038c576122b190516122a3565b503d6123c8565b6040513d85823e3d90fd5b50505090565b612404612137565b80612558575b50426008556124176121f2565b61242042612181565b602082015164ffffffffff808216928116831480612547575b612444575b50505050565b7affffffffffffffffffffffffffffffffffffffffffffffffffffff60408561253b937fc32a546ed958490e37f30335e501e0a39438cb650a4851bfd4b775490af29dad97511694857fffffffffffffffffffffffffffffffffffffffffffff0000000000000000000069ffffffffff00000000006006549360281b1692161717600655015116807fffffffffff0000000000000000000000000000000000000000000000000000006007541617600755604051938493849193927affffffffffffffffffffffffffffffffffffffffffffffffffffff90604092606085019664ffffffffff809216865216602085015216910152565b0390a13880808061243e565b50828160065460281c161415612439565b6020816125887fb9d196a585c1a894f648393ec7d52cc59ff6d94191579d073ba32b0a74d7f7a693600954612199565b600955604051908152a13861240a565b906125a16123fc565b6125aa82611dd8565b9182156126435761263d916125e18230337f0000000000000000000000000000000000000000000000000000000000000000612707565b6125eb84826126a1565b73ffffffffffffffffffffffffffffffffffffffff6040519183835285602084015216907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d760403392a3600954612199565b60095590565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152fd5b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602073ffffffffffffffffffffffffffffffffffffffff6000936126e886600254612199565b60025516938484526003825260408420818154019055604051908152a3565b9160008093602095606494604051947f23b872dd00000000000000000000000000000000000000000000000000000000865273ffffffffffffffffffffffffffffffffffffffff809216600487015216602485015260448401525af13d15601f3d116001600051141617161561277957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602073ffffffffffffffffffffffffffffffffffffffff6000941692838552600382526040852061282b828254611e61565b90558060025403600255604051908152a3565b6000918260449260209573ffffffffffffffffffffffffffffffffffffffff604051947fa9059cbb00000000000000000000000000000000000000000000000000000000865216600485015260248401525af13d15601f3d11600160005114161716156128a757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b919091612933837affffffffffffffffffffffffffffffffffffffffffffffffffffff6040840151166121df565b64ffffffffff918260208183511692015116900391808311611e6e5761296b61297d9261297492670de0b6b3a76400009516906121a6565b94600c546121df565b600954906121df565b048083116129885750565b915056290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6000000000000000000000000853d955acef822db058eb8505911ed77f175b99e00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000093a8000000000000000000000000000000000000000000000000000000000b4241eae000000000000000000000000831822660572bd54ebaa065c2acef662a6277d40000000000000000000000000000000000000000000000000000000000000000b5374616b6564204652415800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057346524158000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6040608081526004908136101561001557600080fd5b600091823560e01c91826301e1d11414611a4c57826306fdde031461198657826307a2d13a146112f0578263090f3f5014611933578263095ea7b3146118985782630a28a4771461185b57826318160ddd1461181e57826323b872dd146117055782632af98d6d146116c8578263313ce5671461166c578263358245fc146116315782633644e515146115f6578263374010a41461155157826338d52e0f146114e2578263402d267d14610787578263450140951461143457826348f76e0f146113805782634bc66f321461132d5782634cdad506146112f05782634f8b4ae71461121c57826356caf605146111c35782635ebae5661461114957826361c1c5e91461110c5782636e553f65146110c757826370a082311461039057826375e077c314610f395782637ecebe0014610ed75782638f765d5914610e5b57826394bf804d14610d7957826395d89b4114610c6357826399530b0614610c07578263a80e754714610bae578263a9059cbb14610b01578263aaf5eb6814610ac0578263b3d7f6b914610a83578263b460af9414610963578263ba087652146107c9578263bd6f36031461078c578263c63d75b614610787578263c6e6f592146102d4578263ce96cb7714610724578263d505accf14610430578263d71356e6146103f6578263d905777e14610390578263dd62ed3e14610318578263ef8b30f7146102d457505063f6ccaad41461022957600080fd5b346102d157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15761025f611d66565b7fffffffffffffffffffffffff000000000000000000000000000000000000000080600a5416600a55600b54903373ffffffffffffffffffffffffffffffffffffffff83167f31b6c5a04b069b6ec1b3cef44c4e7c1eadd721349cda9823d0b1877b3551cdc68580a3163317600b5580f35b80fd5b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1575061031160209235611dd8565b9051908152f35b91503461038c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261038c576020928291610355611bd7565b61035d611bff565b9173ffffffffffffffffffffffffffffffffffffffff8092168452865283832091168252845220549051908152f35b8280fd5b8390346103f25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2578060209273ffffffffffffffffffffffffffffffffffffffff6103e2611bd7565b1681526003845220549051908152f35b5080fd5b83346102d157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15761042d6123fc565b80f35b8390346103f25760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257610469611bd7565b90610472611bff565b9160443560643592610482611c84565b924285106106c757610492611e9d565b9573ffffffffffffffffffffffffffffffffffffffff8092169586895260209560058752848a209889549960018b01905585519285898501957f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c987528b89870152169a8b606086015288608086015260a085015260c084015260c0835260e0830167ffffffffffffffff948482108683111761069a57818852845190206101008501927f19010000000000000000000000000000000000000000000000000000000000008452610102860152610122850152604281526101608401948186109086111761066e57848752519020835260ff1661018082015260a4356101a082015260c4356101c0909101528780528490889060809060015afa1561066457865116968715158061065b575b156106005786977f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259596975283528087208688528352818188205551908152a380f35b8360649251917f08c379a0000000000000000000000000000000000000000000000000000000008352820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152fd5b508488146105bd565b81513d88823e3d90fd5b60248c60418f7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5060248c60418f7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b60648860208451917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152fd5b8390346103f25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576103118160209373ffffffffffffffffffffffffffffffffffffffff610779611bd7565b168152600385522054611dfd565b611c22565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020906008549051908152f35b9083346102d1576107d936611c94565b6107e5939291936123fc565b73ffffffffffffffffffffffffffffffffffffffff90818116938433036108f8575b5061081183611dfd565b95861561089b57509282602097959261083d88956108959761083588600954611e61565b6009556127d7565b865191858352898301528316907ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db873392a47f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e61283e565b51908152f35b60649060208951917f08c379a0000000000000000000000000000000000000000000000000000000008352820152600b60248201527f5a45524f5f4153534554530000000000000000000000000000000000000000006044820152fd5b848152866020528781203382526020528780822054857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361093e575b505050610807565b61094791611e61565b9186815288602052818120338252602052205587878185610936565b8390346103f25761089560209361097936611c94565b959091926109856123fc565b6109c561099185611e3f565b809873ffffffffffffffffffffffffffffffffffffffff938985831696873303610a1e575b50505061083587600954611e61565b85519084825287898301528316907ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db873392a47f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e61283e565b878152828e528181203382528e5281812054857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610a60575b50506109b6565b610a6991611e61565b928882528e528181203382528e5220558b89818085610a59565b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1575061031160209235611e1e565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f25760209051670de0b6b3a76400008152f35b8390346103f257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602091610b3c611bd7565b8273ffffffffffffffffffffffffffffffffffffffff6024359233855260038752828520610b6b858254611e61565b90551692838152600386522081815401905582519081527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef843392a35160018152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090517f0000000000000000000000000000000000000000000000000de0b6b3a76400008152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020906103117f0000000000000000000000000000000000000000000000000de0b6b3a7640000611dfd565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f25780519082600190815491610ca683611a92565b918286526020938281169081600014610d395750600114610ce1575b610cdd8686610cd3828b0383611b30565b5191829182611b71565b0390f35b9295508083527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b828410610d265750505082610cdd94610cd3928201019486610cc2565b8054868501880152928601928101610d09565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001687860152505050151560051b8301019250610cd382610cdd86610cc2565b91503461038c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261038c57602092503590610db7611bff565b91610dc06123fc565b73ffffffffffffffffffffffffffffffffffffffff610dde82611e1e565b93610e0b8530337f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e612707565b610e1583826126a1565b8351928584528684015216907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7833392a3610e5282600954612199565b60095551908152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257606090610e966121f2565b907affffffffffffffffffffffffffffffffffffffffffffffffffffff8180519364ffffffffff808251168652602082015116602086015201511690820152f35b8390346103f25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2578060209273ffffffffffffffffffffffffffffffffffffffff610f29611bd7565b1681526005845220549051908152f35b83346102d15760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1578235610f73611bff565b916064359081151582036102d157610f89611c84565b91156110c1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e16803b1561038c579060e48884809460ff8b5198899687957fd505accf00000000000000000000000000000000000000000000000000000000875233908701523060248701526044860152604435606486015216608484015260a43560a484015260c43560c48401525af180156110b55761106a575b6020856103118686612598565b67ffffffffffffffff821161108957508352602093506103118561105d565b806041877f4e487b71000000000000000000000000000000000000000000000000000000006024945252fd5b508451903d90823e3d90fd5b82610fb1565b83346102d157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15750610311602092611105611bff565b9035612598565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020906009549051908152f35b83346102d157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15750600654600754915164ffffffffff808316825260289290921c90911660208201527affffffffffffffffffffffffffffffffffffffffffffffffffffff9091166040820152606090f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090517f0000000000000000000000000000000000000000000000000000000000093a808152f35b83346102d157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d157611253611cf4565b61125b611d66565b7fffffffffffffffffffffffff000000000000000000000000000000000000000080600a5416600a55600b548273ffffffffffffffffffffffffffffffffffffffff821681817f162998b90abc2507f3953aa797827b03a14c42dbd9a35f09feaf02e0d592773a8280a37f31b6c5a04b069b6ec1b3cef44c4e7c1eadd721349cda9823d0b1877b3551cdc68280a316600b5580f35b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1575061031160209235611dfd565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f25760209073ffffffffffffffffffffffffffffffffffffffff600b54169051908152f35b91503461038c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261038c5735906113bb611cf4565b6113c36123fc565b67ffffffffffffffff80831161140c575b507f05d530f0fd6974b7d995fd3b71870f5301bb9fe086180bdd0bd36526728f5c6b90600c548151908152836020820152a1600c5580f35b91507f05d530f0fd6974b7d995fd3b71870f5301bb9fe086180bdd0bd36526728f5c6b6113d4565b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15761146c611bd7565b611474611cf4565b73ffffffffffffffffffffffffffffffffffffffff80911690817fffffffffffffffffffffffff0000000000000000000000000000000000000000600a541617600a55600b54167f162998b90abc2507f3953aa797827b03a14c42dbd9a35f09feaf02e0d592773a8380a380f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e168152f35b83346102d1577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601608081126103f2576060136102d15781519261159584611ae5565b3564ffffffffff90818116810361038c57845260243590811681036103f2576020840152604435907affffffffffffffffffffffffffffffffffffffffffffffffffffff821682036102d15750826103119183602095015260643590612905565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090610311611e9d565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090610311612137565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020905160ff7f0000000000000000000000000000000000000000000000000000000000000012168152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090600c549051908152f35b83346102d15760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d15761173d611bd7565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef611766611bff565b946044358573ffffffffffffffffffffffffffffffffffffffff80951694858752602098848a958652838920338a52865283892054857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036117fb575b505050868852600385528288206117dc858254611e61565b9055169586815260038452208181540190558551908152a35160018152f35b61180491611e61565b90888a528652838920338a528652838920558a80856117c4565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2576020906002549051908152f35b83346102d15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d1575061031160209235611e3f565b91503461038c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261038c576020926118d3611bd7565b9183602435928392338252875273ffffffffffffffffffffffffffffffffffffffff8282209516948582528752205582519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925843392a35160018152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f25760209073ffffffffffffffffffffffffffffffffffffffff600a54169051908152f35b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f2578051908280546119c581611a92565b908185526020926001918281169081600014610d3957506001146119f457610cdd8686610cd3828b0383611b30565b8080949750527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b828410611a395750505082610cdd94610cd3928201019486610cc2565b8054868501880152928601928101611a1c565b8390346103f257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103f257602090610311611a8a612137565b600954612199565b90600182811c92168015611adb575b6020831014611aac57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691611aa1565b6060810190811067ffffffffffffffff821117611b0157604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611b0157604052565b60208082528251818301819052939260005b858110611bc3575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201611b83565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203611bfa57565b600080fd5b6024359073ffffffffffffffffffffffffffffffffffffffff82168203611bfa57565b34611bfa5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112611bfa57611c59611bd7565b5060206040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8152f35b6084359060ff82168203611bfa57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6060910112611bfa576004359073ffffffffffffffffffffffffffffffffffffffff906024358281168103611bfa57916044359081168103611bfa5790565b73ffffffffffffffffffffffffffffffffffffffff600b5416803303611d175750565b6040517f443dc2b400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b73ffffffffffffffffffffffffffffffffffffffff600a5416803303611d895750565b6040517fbe5a953700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b60025480611de4575090565b90611dfa91611df4611a8a612137565b9161207a565b90565b60025480611e09575090565b611dfa91611e18611a8a612137565b9061207a565b60025480611e2a575090565b611dfa91611e39611a8a612137565b906120ae565b60025480611e4b575090565b90611dfa91611e5b611a8a612137565b916120ae565b91908203918211611e6e57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000467f000000000000000000000000000000000000000000000000000000000000000103611eeb57507f9215534876c4f9b64ee9cbc7b70915f8def0270d6a0aacd514b3ccd16f7e1fe490565b60405181548291611efb82611a92565b808252816020948582019460019087828216918260001461203e575050600114611fe5575b50611f2d92500382611b30565b51902091604051918201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f845260408301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a083015260a0825260c082019082821067ffffffffffffffff831117611fb8575060405251902090565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526041600452fd5b87805286915087907f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b858310612026575050611f2d935082010138611f20565b8054838801850152869450889390920191810161200f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168852611f2d95151560051b8501019250389150611f209050565b817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048111820215830215611bfa57020490565b817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048111820215830215611bfa570290808204910615150190565b604051906120f782611ae5565b8160065464ffffffffff90818116835260281c16602082015260407affffffffffffffffffffffffffffffffffffffffffffffffffffff60075416910152565b611dfa6121426120ea565b60085461214e42612181565b825164ffffffffff92918316908316811115612177575061217191835116611e61565b90612905565b6121719250611e61565b65010000000000811015611bfa5764ffffffffff1690565b91908201809211611e6e57565b81156121b0570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b81810292918115918404141715611e6e57565b6040516121fe81611ae5565b600090818152816040602092828482015201526122196120ea565b9164ffffffffff91828451164211156123f6576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152818160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e165afa80156123eb5783906123bc575b6122b1915060095490611e61565b7f0000000000000000000000000000000000000000000000000000000000093a80926122f76122f2856122ed816122e88142612199565b6121a6565b6121df565b612181565b9385808616916123074284611e61565b6028820411612370575b5050507b010000000000000000000000000000000000000000000000000000008210156102d157507affffffffffffffffffffffffffffffffffffffffffffffffffffff1660408501528261236542612181565b169084015216815290565b61237d9192939650612181565b160184811161238f5792388581612311565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b508181813d83116123e4575b6123d28183611b30565b8101031261038c576122b190516122a3565b503d6123c8565b6040513d85823e3d90fd5b50505090565b612404612137565b80612558575b50426008556124176121f2565b61242042612181565b602082015164ffffffffff808216928116831480612547575b612444575b50505050565b7affffffffffffffffffffffffffffffffffffffffffffffffffffff60408561253b937fc32a546ed958490e37f30335e501e0a39438cb650a4851bfd4b775490af29dad97511694857fffffffffffffffffffffffffffffffffffffffffffff0000000000000000000069ffffffffff00000000006006549360281b1692161717600655015116807fffffffffff0000000000000000000000000000000000000000000000000000006007541617600755604051938493849193927affffffffffffffffffffffffffffffffffffffffffffffffffffff90604092606085019664ffffffffff809216865216602085015216910152565b0390a13880808061243e565b50828160065460281c161415612439565b6020816125887fb9d196a585c1a894f648393ec7d52cc59ff6d94191579d073ba32b0a74d7f7a693600954612199565b600955604051908152a13861240a565b906125a16123fc565b6125aa82611dd8565b9182156126435761263d916125e18230337f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e612707565b6125eb84826126a1565b73ffffffffffffffffffffffffffffffffffffffff6040519183835285602084015216907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d760403392a3600954612199565b60095590565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152fd5b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602073ffffffffffffffffffffffffffffffffffffffff6000936126e886600254612199565b60025516938484526003825260408420818154019055604051908152a3565b9160008093602095606494604051947f23b872dd00000000000000000000000000000000000000000000000000000000865273ffffffffffffffffffffffffffffffffffffffff809216600487015216602485015260448401525af13d15601f3d116001600051141617161561277957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602073ffffffffffffffffffffffffffffffffffffffff6000941692838552600382526040852061282b828254611e61565b90558060025403600255604051908152a3565b6000918260449260209573ffffffffffffffffffffffffffffffffffffffff604051947fa9059cbb00000000000000000000000000000000000000000000000000000000865216600485015260248401525af13d15601f3d11600160005114161716156128a757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b919091612933837affffffffffffffffffffffffffffffffffffffffffffffffffffff6040840151166121df565b64ffffffffff918260208183511692015116900391808311611e6e5761296b61297d9261297492670de0b6b3a76400009516906121a6565b94600c546121df565b600954906121df565b048083116129885750565b915056

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

000000000000000000000000853d955acef822db058eb8505911ed77f175b99e00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000093a8000000000000000000000000000000000000000000000000000000000b4241eae000000000000000000000000831822660572bd54ebaa065c2acef662a6277d40000000000000000000000000000000000000000000000000000000000000000b5374616b6564204652415800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057346524158000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _underlying (address): 0x853d955aCEf822Db058eb8505911ED77F175b99e
Arg [1] : _name (string): Staked FRAX
Arg [2] : _symbol (string): sFRAX
Arg [3] : _rewardsCycleLength (uint32): 604800
Arg [4] : _maxDistributionPerSecondPerAsset (uint256): 3022266030
Arg [5] : _timelockAddress (address): 0x831822660572bd54ebaa065C2acef662a6277D40

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 000000000000000000000000853d955acef822db058eb8505911ed77f175b99e
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [3] : 0000000000000000000000000000000000000000000000000000000000093a80
Arg [4] : 00000000000000000000000000000000000000000000000000000000b4241eae
Arg [5] : 000000000000000000000000831822660572bd54ebaa065c2acef662a6277d40
Arg [6] : 000000000000000000000000000000000000000000000000000000000000000b
Arg [7] : 5374616b65642046524158000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [9] : 7346524158000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

OVERVIEW

Staked FRAX (sFRAX) is an ERC4626 staking vault that distributes part of the Frax Protocol yield weekly to stakers denominated in FRAX stablecoins.

Validator Index Block Amount
View All Withdrawals

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

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