ETH Price: $3,480.39 (+2.59%)

Contract

0xF5C6d9Fc73991F687f158FE30D4A77691a9Fd4d8
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Approve214409662024-12-20 3:12:235 days ago1734664343IN
HOPE: stHOPE Token
0 ETH0.000446749.69841594
Approve213050382024-12-01 3:41:3523 days ago1733024495IN
HOPE: stHOPE Token
0 ETH0.000197488.2012985
Transfer212419262024-11-22 7:57:4732 days ago1732262267IN
HOPE: stHOPE Token
0 ETH0.01215219.1672571
Approve211048682024-11-03 4:57:2351 days ago1730609843IN
HOPE: stHOPE Token
0 ETH0.000185454
Approve210206872024-10-22 11:01:4763 days ago1729594907IN
HOPE: stHOPE Token
0 ETH0.000199628.29022886
Approve209553792024-10-13 8:11:3572 days ago1728807095IN
HOPE: stHOPE Token
0 ETH0.0002777811.5357476
Approve208713262024-10-01 14:45:2384 days ago1727793923IN
HOPE: stHOPE Token
0 ETH0.0005599823.25510299
Approve208510902024-09-28 19:01:3587 days ago1727550095IN
HOPE: stHOPE Token
0 ETH0.000175937.3064318
Approve208397522024-09-27 5:05:2388 days ago1727413523IN
HOPE: stHOPE Token
0 ETH0.0002693211.18462519
Approve207681702024-09-17 5:13:3598 days ago1726550015IN
HOPE: stHOPE Token
0 ETH0.000108392.3532194
Approve206759762024-09-04 8:19:11111 days ago1725437951IN
HOPE: stHOPE Token
0 ETH0.000077181.67550023
Approve205581732024-08-18 21:22:47128 days ago1724016167IN
HOPE: stHOPE Token
0 ETH0.000048331.04250174
Approve202227582024-07-03 1:39:23175 days ago1719970763IN
HOPE: stHOPE Token
0 ETH0.000100972.17791062
Redeem All201821722024-06-27 9:36:47180 days ago1719481007IN
HOPE: stHOPE Token
0 ETH0.009028225.08134842
Redeem All200841122024-06-13 16:31:59194 days ago1718296319IN
HOPE: stHOPE Token
0 ETH0.056604723.36535692
Unstaking199601742024-05-27 9:00:23211 days ago1716800423IN
HOPE: stHOPE Token
0 ETH0.0057761211.49854244
Redeem All199491882024-05-25 20:10:23213 days ago1716667823IN
HOPE: stHOPE Token
0 ETH0.002018284.50283184
Unstaking199448312024-05-25 5:34:35213 days ago1716615275IN
HOPE: stHOPE Token
0 ETH0.00393243.34776207
Redeem All199153282024-05-21 2:33:59218 days ago1716258839IN
HOPE: stHOPE Token
0 ETH0.0048020510.96216171
Redeem All198916112024-05-17 18:55:59221 days ago1715972159IN
HOPE: stHOPE Token
0 ETH0.002227514.34982721
Unstaking198738602024-05-15 7:23:23223 days ago1715757803IN
HOPE: stHOPE Token
0 ETH0.002600525.1393042
Redeem All198368682024-05-10 3:10:47229 days ago1715310647IN
HOPE: stHOPE Token
0 ETH0.001563923.38961681
Approve198293092024-05-09 1:48:11230 days ago1715219291IN
HOPE: stHOPE Token
0 ETH0.000096854.02236954
Approve197977702024-05-04 15:55:47234 days ago1714838147IN
HOPE: stHOPE Token
0 ETH0.000305146.58159164
Approve197772652024-05-01 19:06:47237 days ago1714590407IN
HOPE: stHOPE Token
0 ETH0.0008591318.53017341
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StakingHOPE

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 18 : StakingHOPE.sol
// SPDX-License-Identifier: LGPL-3.0

pragma solidity 0.8.17;

import "./interfaces/IStaking.sol";
import "./gauges/AbsGauge.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {TransferHelper} from "light-lib/contracts/TransferHelper.sol";

contract StakingHOPE is IStaking, ERC20, AbsGauge {
    uint256 internal constant _LOCK_TIME = 28;

    // permit2 contract
    address public permit2Address;

    struct UnstakingOrderDetail {
        uint256 amount;
        uint256 redeemTime;
        bool redeemExecuted;
    }

    struct UnstakingOrderSummary {
        uint256 notRedeemAmount;
        uint256 index;
        mapping(uint256 => UnstakingOrderDetail) orderMap;
    }

    uint256 public totalNotRedeemAmount;
    mapping(address => UnstakingOrderSummary) public unstakingMap;
    mapping(uint256 => uint256) public unstakingDayHistory;
    uint256 private _unstakeTotal;

    constructor(address _stakedToken, address _minter, address _permit2Address) ERC20("staked HOPE", "stHOPE") {
        require(_stakedToken != address(0), "CE000");
        require(_permit2Address != address(0), "CE000");

        _init(_stakedToken, _minter, _msgSender());

        permit2Address = _permit2Address;
    }

    /***
     * @notice Stake HOPE to get stHOPE
     *
     * @param amount
     * @param nonce
     * @param deadline
     * @param signature
     */
    function staking(uint256 amount, uint256 nonce, uint256 deadline, bytes memory signature) external override returns (bool) {
        require(amount != 0, "CE002");

        address staker = _msgSender();
        // checking amount
        uint256 balanceOfUser = IERC20(lpToken).balanceOf(staker);
        require(balanceOfUser >= amount, "CE002");
        TransferHelper.doTransferIn(permit2Address, lpToken, amount, staker, nonce, deadline, signature);

        _checkpoint(staker);

        _mint(staker, amount);

        _updateLiquidityLimit(staker, lpBalanceOf(staker), lpTotalSupply());

        emit Staking(staker, amount);
        return true;
    }

    /***
     * @notice unstaking the staked amount
     * The unstaking process takes 28 days to complete. During this period,
     *  the unstaked $HOPE cannot be traded, and no staking rewards are accrued.
     *
     * @param
     * @return
     */
    function unstaking(uint256 amount) external {
        require(amount != 0, "CE002");

        address staker = _msgSender();
        // checking amount
        uint256 balanceOfUser = lpBalanceOf(staker);
        require(balanceOfUser >= amount, "CE002");

        _checkpoint(staker);

        uint256 nextDayTime = ((block.timestamp + _DAY) / _DAY) * _DAY;
        // lock 28 days
        uint256 redeemTime = nextDayTime + _DAY * _LOCK_TIME;

        unstakingDayHistory[nextDayTime] = unstakingDayHistory[nextDayTime] + amount;
        _unstakeTotal = _unstakeTotal + amount;

        UnstakingOrderSummary storage summaryMap = unstakingMap[staker];

        summaryMap.notRedeemAmount = summaryMap.notRedeemAmount + amount;
        summaryMap.index = summaryMap.index + 1;
        summaryMap.orderMap[summaryMap.index] = UnstakingOrderDetail(amount, redeemTime, false);
        totalNotRedeemAmount += amount;

        _updateLiquidityLimit(staker, lpBalanceOf(staker), lpTotalSupply());
        emit Unstaking(staker, amount);
    }

    /***
     * @notice get unstaking amount
     *
     * @return
     */
    function unstakingBalanceOf(address _addr) public view returns (uint256) {
        uint256 _unstakingAmount = 0;
        UnstakingOrderSummary storage summaryMap = unstakingMap[_addr];
        for (uint256 _index = summaryMap.index; _index > 0; _index--) {
            if (summaryMap.orderMap[_index].redeemExecuted) {
                break;
            }
            if (block.timestamp < summaryMap.orderMap[_index].redeemTime) {
                _unstakingAmount += summaryMap.orderMap[_index].amount;
            }
        }
        return _unstakingAmount;
    }

    function unstakingTotal() public view returns (uint256) {
        uint256 _unstakingTotal = 0;

        uint256 nextDayTime = ((block.timestamp + _DAY) / _DAY) * _DAY;
        for (uint256 i = 0; i < _LOCK_TIME; i++) {
            _unstakingTotal += unstakingDayHistory[nextDayTime - _DAY * i];
        }
        return _unstakingTotal;
    }

    /***
     * @notice get can redeem amount
     *
     * @param
     * @return
     */
    function unstakedBalanceOf(address _addr) public view returns (uint256) {
        uint256 amountToRedeem = 0;
        UnstakingOrderSummary storage summaryMap = unstakingMap[_addr];
        for (uint256 _index = summaryMap.index; _index > 0; _index--) {
            if (summaryMap.orderMap[_index].redeemExecuted) {
                break;
            }
            if (block.timestamp >= summaryMap.orderMap[_index].redeemTime) {
                amountToRedeem += summaryMap.orderMap[_index].amount;
            }
        }
        return amountToRedeem;
    }

    function unstakedTotal() external view returns (uint256) {
        return _unstakeTotal - unstakingTotal();
    }

    /***
     * @notice Redeem all amounts to your account
     *
     * @param
     * @return
     */
    function redeemAll() external override returns (uint256) {
        address redeemer = _msgSender();
        uint256 amountToRedeem = unstakedBalanceOf(redeemer);
        require(amountToRedeem != 0, "CE002");

        _checkpoint(redeemer);

        UnstakingOrderSummary storage summaryMap = unstakingMap[redeemer];
        for (uint256 _index = summaryMap.index; _index > 0; _index--) {
            if (summaryMap.orderMap[_index].redeemExecuted) {
                break;
            }
            if (block.timestamp >= summaryMap.orderMap[_index].redeemTime) {
                uint256 amount = summaryMap.orderMap[_index].amount;
                summaryMap.orderMap[_index].redeemExecuted = true;
                summaryMap.notRedeemAmount = summaryMap.notRedeemAmount - amount;
                totalNotRedeemAmount -= amount;
            }
        }

        _burn(redeemer, amountToRedeem);
        TransferHelper.doTransferOut(lpToken, redeemer, amountToRedeem);
        _updateLiquidityLimit(redeemer, lpBalanceOf(redeemer), lpTotalSupply());

        _unstakeTotal = _unstakeTotal - amountToRedeem;

        emit Redeem(redeemer, amountToRedeem);
        return amountToRedeem;
    }

    /***
     * @notice redeem amount by index(Prevent the number of unstaking too much to redeem)
     *
     * @param maxIndex
     * @return
     */
    function redeemByMaxIndex(uint256 maxIndex) external returns (uint256) {
        address redeemer = _msgSender();

        uint256 allToRedeemAmount = unstakedBalanceOf(redeemer);
        require(allToRedeemAmount != 0, "CE002");

        uint256 amountToRedeem = 0;
        _checkpoint(redeemer);

        UnstakingOrderSummary storage summaryMap = unstakingMap[redeemer];
        uint256 indexCount = 0;
        for (uint256 _index = 1; _index <= summaryMap.index; _index++) {
            if (indexCount >= maxIndex) {
                break;
            }
            if (block.timestamp >= summaryMap.orderMap[_index].redeemTime && !summaryMap.orderMap[_index].redeemExecuted) {
                uint256 amount = summaryMap.orderMap[_index].amount;
                amountToRedeem += amount;
                summaryMap.orderMap[_index].redeemExecuted = true;
                summaryMap.notRedeemAmount = summaryMap.notRedeemAmount - amount;
                totalNotRedeemAmount -= amount;
                indexCount++;
            }
        }

        if (amountToRedeem > 0) {
            _burn(redeemer, amountToRedeem);
            TransferHelper.doTransferOut(lpToken, redeemer, amountToRedeem);
            _updateLiquidityLimit(redeemer, lpBalanceOf(redeemer), lpTotalSupply());

            _unstakeTotal = _unstakeTotal - amountToRedeem;
            emit Redeem(redeemer, amountToRedeem);
        }
        return amountToRedeem;
    }

    /**
     * @dev Set permit2 address, onlyOwner
     * @param newAddress New permit2 address
     */
    function setPermit2Address(address newAddress) external onlyOwner {
        require(newAddress != address(0), "CE000");
        address oldAddress = permit2Address;
        permit2Address = newAddress;
        emit SetPermit2Address(oldAddress, newAddress);
    }

    function lpBalanceOf(address _addr) public view override returns (uint256) {
        return super.balanceOf(_addr) - unstakingMap[_addr].notRedeemAmount;
    }

    function lpTotalSupply() public view override returns (uint256) {
        return super.totalSupply() - totalNotRedeemAmount;
    }

    /***
     * @notice Transfers Gauge deposit (stHOPE) from the caller to _to.
     *
     * @param to
     * @param amount
     * @return bool
     */
    function transfer(address _to, uint256 _amount) public virtual override returns (bool) {
        address owner = _msgSender();
        uint256 fromBalance = lpBalanceOf(owner);
        require(fromBalance >= _amount, "CE002");

        _checkpoint(owner);
        _checkpoint(_to);

        bool result = super.transfer(_to, _amount);

        _updateLiquidityLimit(owner, lpBalanceOf(owner), lpTotalSupply());
        _updateLiquidityLimit(_to, lpBalanceOf(_to), lpTotalSupply());
        return result;
    }

    /***
     * @notice Tansfers a Gauge deposit between _from and _to.
     *
     * @param from
     * @param to
     * @param amount
     * @return bool
     */
    function transferFrom(address _from, address _to, uint256 _amount) public override returns (bool) {
        uint256 fromBalance = lpBalanceOf(_from);
        require(fromBalance >= _amount, "CE002");

        _checkpoint(_from);
        _checkpoint(_to);

        bool result = super.transferFrom(_from, _to, _amount);

        _updateLiquidityLimit(_from, lpBalanceOf(_from), lpTotalSupply());
        _updateLiquidityLimit(_to, lpBalanceOf(_to), lpTotalSupply());
        return result;
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

File 3 of 18 : Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() external {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }
}

File 4 of 18 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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].
 *
 * 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}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    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 value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

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

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

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

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

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

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

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

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

        return true;
    }

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

        _beforeTokenTransfer(from, to, amount);

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

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 5 of 18 : 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 6 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 7 of 18 : 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 8 of 18 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

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

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

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

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

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

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

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 9 of 18 : AbsGauge.sol
// SPDX-License-Identifier: LGPL-3.0

pragma solidity 0.8.17;

import "@openzeppelin/contracts/access/Ownable2Step.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import "../interfaces/ILT.sol";
import "../interfaces/IGaugeController.sol";
import "../interfaces/IVotingEscrow.sol";
import "../interfaces/IMinter.sol";
import "light-lib/contracts/LibTime.sol";

abstract contract AbsGauge is Ownable2Step {
    event Deposit(address indexed provider, uint256 value);
    event Withdraw(address indexed provider, uint256 value);
    event UpdateLiquidityLimit(
        address user,
        uint256 originalBalance,
        uint256 originalSupply,
        uint256 workingBalance,
        uint256 workingSupply,
        uint256 votingBalance,
        uint256 votingTotal
    );
    event SetPermit2Address(address oldAddress, address newAddress);

    uint256 internal constant _TOKENLESS_PRODUCTION = 40;
    uint256 internal constant _DAY = 86400;
    uint256 internal constant _WEEK = _DAY * 7;

    bool public isKilled;
    // pool lp token
    address public lpToken;

    //Contracts
    IMinter public minter;
    // lt_token
    ILT public ltToken;
    //IERC20 public template;
    IGaugeController public controller;
    IVotingEscrow public votingEscrow;

    uint256 public futureEpochTime;

    mapping(address => uint256) public workingBalances;
    uint256 public workingSupply;

    // The goal is to be able to calculate ∫(rate * balance / totalSupply dt) from 0 till checkpoint
    // All values are kept in units of being multiplied by 1e18
    uint256 public period; //modified from "int256 public period" since it never be minus.

    // uint256[100000000000000000000000000000] public period_timestamp;
    mapping(uint256 => uint256) public periodTimestamp;

    //uint256[100_000_000_000_000_000_000_000_000_000] public periodTimestamp;

    // 1e18 * ∫(rate(t) / totalSupply(t) dt) from 0 till checkpoint
    // bump epoch when rate() changes
    mapping(uint256 => uint256) integrateInvSupply;

    // 1e18 * ∫(rate(t) / totalSupply(t) dt) from (last_action) till checkpoint
    mapping(address => uint256) public integrateInvSupplyOf;
    mapping(address => uint256) public integrateCheckpointOf;

    // ∫(balance * rate(t) / totalSupply(t) dt) from 0 till checkpoint
    // Units rate * t = already number of coins per address to issue
    mapping(address => uint256) public integrateFraction; //Mintable Token amount (include minted amount)

    uint256 public inflationRate;

    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    function _init(address _lpAddr, address _minter, address _owner) internal {
        require(_lpAddr != address(0), "CE000");
        require(_minter != address(0), "CE000");
        require(_owner != address(0), "CE000");
        require(!_initialized, "Initializable: contract is already initialized");
        _initialized = true;

        _transferOwnership(_owner);

        lpToken = _lpAddr;
        minter = IMinter(_minter);
        address _ltToken = minter.token();
        ltToken = ILT(_ltToken);
        controller = IGaugeController(minter.controller());
        votingEscrow = IVotingEscrow(controller.votingEscrow());
        periodTimestamp[0] = block.timestamp;
        inflationRate = ltToken.rate();
        futureEpochTime = ltToken.futureEpochTimeWrite();
    }

    /***
     * @notice Calculate limits which depend on the amount of lp Token per-user.
     *        Effectively it calculates working balances to apply amplification
     *        of LT production by LT
     * @param _addr User address
     * @param _l User's amount of liquidity (LP tokens)
     * @param _L Total amount of liquidity (LP tokens)
     */
    function _updateLiquidityLimit(address _addr, uint256 _l, uint256 _L) internal {
        // To be called after totalSupply is updated
        uint256 _votingBalance = votingEscrow.balanceOfAtTime(_addr, block.timestamp);
        uint256 _votingTotal = votingEscrow.totalSupplyAtTime(block.timestamp);

        uint256 _lim = (_l * _TOKENLESS_PRODUCTION) / 100;
        if (_votingTotal > 0) {
            // 0.4 * _l + 0.6 * _L * balance/total
            _lim += (_L * _votingBalance * (100 - _TOKENLESS_PRODUCTION)) / _votingTotal / 100;
        }

        _lim = Math.min(_l, _lim);
        uint256 _oldBal = workingBalances[_addr];
        workingBalances[_addr] = _lim;
        uint256 _workingSupply = workingSupply + _lim - _oldBal;
        workingSupply = _workingSupply;

        emit UpdateLiquidityLimit(_addr, _l, _L, _lim, _workingSupply, _votingBalance, _votingTotal);
    }

    //to avoid "stack too deep"
    struct CheckPointParameters {
        uint256 _period;
        uint256 _periodTime;
        uint256 _integrateInvSupply;
        uint256 rate;
        uint256 newRate;
        uint256 prevFutureEpoch;
    }

    /***
     * @notice Checkpoint for a user
     * @param _addr User address
     *
     *This function does,
     *1. Calculate Iis for All: Calc and add Iis for every week. Iis only increses over time.
     *2. Calculate Iu for _addr: Calc by (defferece between Iis(last time) and Iis(this time))* LP deposit amount of _addr(include  locking boost)
     *
     * working_supply & working_balance = total_supply & total_balance with  locking boost。
     * Check whitepaper about Iis and Iu.
     */
    function _checkpoint(address _addr) internal {
        CheckPointParameters memory _st;

        _st._period = period;
        _st._periodTime = periodTimestamp[_st._period];
        _st._integrateInvSupply = integrateInvSupply[_st._period];
        _st.rate = inflationRate;
        _st.newRate = _st.rate;
        _st.prevFutureEpoch = futureEpochTime;
        if (_st.prevFutureEpoch >= _st._periodTime) {
            //update future_epoch_time & inflation_rate
            futureEpochTime = ltToken.futureEpochTimeWrite();
            _st.newRate = ltToken.rate();
            inflationRate = _st.newRate;
        }
        controller.checkpointGauge(address(this));

        if (isKilled) {
            // Stop distributing inflation as soon as killed
            _st.rate = 0;
        }

        // Update integral of 1/supply
        if (block.timestamp > _st._periodTime) {
            uint256 _workingSupply = workingSupply;
            uint256 _prevWeekTime = _st._periodTime;
            uint256 _weekTime = Math.min(LibTime.timesRoundedByWeek(_st._periodTime + _WEEK), block.timestamp);
            for (uint256 i; i < 500; i++) {
                uint256 _dt = _weekTime - _prevWeekTime;
                uint256 _w = controller.gaugeRelativeWeight(address(this), LibTime.timesRoundedByWeek(_prevWeekTime));

                if (_workingSupply > 0) {
                    if (_st.prevFutureEpoch >= _prevWeekTime && _st.prevFutureEpoch < _weekTime) {
                        // If we went across one or multiple epochs, apply the rate
                        // of the first epoch until it ends, and then the rate of
                        // the last epoch.
                        // If more than one epoch is crossed - the gauge gets less,
                        // but that'd meen it wasn't called for more than 1 year
                        _st._integrateInvSupply += (_st.rate * _w * (_st.prevFutureEpoch - _prevWeekTime)) / _workingSupply;
                        _st.rate = _st.newRate;
                        _st._integrateInvSupply += (_st.rate * _w * (_weekTime - _st.prevFutureEpoch)) / _workingSupply;
                    } else {
                        _st._integrateInvSupply += (_st.rate * _w * _dt) / _workingSupply;
                    }
                    // On precisions of the calculation
                    // rate ~= 10e18
                    // last_weight > 0.01 * 1e18 = 1e16 (if pool weight is 1%)
                    // _working_supply ~= TVL * 1e18 ~= 1e26 ($100M for example)
                    // The largest loss is at dt = 1
                    // Loss is 1e-9 - acceptable
                }
                if (_weekTime == block.timestamp) {
                    break;
                }
                _prevWeekTime = _weekTime;
                _weekTime = Math.min(_weekTime + _WEEK, block.timestamp);
            }
        }

        _st._period += 1;
        period = _st._period;
        periodTimestamp[_st._period] = block.timestamp;
        integrateInvSupply[_st._period] = _st._integrateInvSupply;

        uint256 _workingBalance = workingBalances[_addr];
        // Update user-specific integrals
        // Calc the ΔIu of _addr and add it to Iu.
        integrateFraction[_addr] += (_workingBalance * (_st._integrateInvSupply - integrateInvSupplyOf[_addr])) / 10 ** 18;
        integrateInvSupplyOf[_addr] = _st._integrateInvSupply;
        integrateCheckpointOf[_addr] = block.timestamp;
    }

    /***
     * @notice Record a checkpoint for `_addr`
     * @param _addr User address
     * @return bool success
     */
    function userCheckpoint(address _addr) external returns (bool) {
        require((msg.sender == _addr) || (msg.sender == address(minter)), "GP000");
        _checkpoint(_addr);
        _updateLiquidityLimit(_addr, lpBalanceOf(_addr), lpTotalSupply());
        return true;
    }

    /***
     * @notice Get the number of claimable tokens per user
     * @dev This function should be manually changed to "view" in the ABI
     * @return uint256 number of claimable tokens per user
     */
    function claimableTokens(address _addr) external returns (uint256) {
        _checkpoint(_addr);
        return (integrateFraction[_addr] - minter.minted(_addr, address(this)));
    }

    /***
     * @notice Kick `_addr` for abusing their boost
     * @dev Only if either they had another voting event, or their voting escrow lock expired
     * @param _addr Address to kick
     */
    function kick(address _addr) external {
        uint256 _tLast = integrateCheckpointOf[_addr];
        uint256 _tVe = votingEscrow.userPointHistoryTs(_addr, votingEscrow.userPointEpoch(_addr));
        uint256 _balance = lpBalanceOf(_addr);

        require(votingEscrow.balanceOfAtTime(_addr, block.timestamp) == 0 || _tVe > _tLast, "GP001");
        require(workingBalances[_addr] > (_balance * _TOKENLESS_PRODUCTION) / 100, "GP001");

        _checkpoint(_addr);
        _updateLiquidityLimit(_addr, lpBalanceOf(_addr), lpTotalSupply());
    }

    function integrateCheckpoint() external view returns (uint256) {
        return periodTimestamp[period];
    }

    /***
     * @notice Set the killed status for this contract
     * @dev When killed, the gauge always yields a rate of 0 and so cannot mint LT
     * @param _is_killed Killed status to set
     */
    function setKilled(bool _isKilled) external onlyOwner {
        isKilled = _isKilled;
    }

    /***
     * @notice The total amount of LP tokens that are currently deposited into the Gauge.
     */
    function lpBalanceOf(address _addr) public view virtual returns (uint256);

    /***
     * @notice The total amount of LP tokens that are currently deposited into the Gauge.
     */
    function lpTotalSupply() public view virtual returns (uint256);
}

File 10 of 18 : IGaugeController.sol
// SPDX-License-Identifier: LGPL-3.0

pragma solidity 0.8.17;

interface IGaugeController {
    struct Point {
        uint256 bias;
        uint256 slope;
    }

    struct VotedSlope {
        uint256 slope;
        uint256 power;
        uint256 end;
    }

    struct UserPoint {
        uint256 bias;
        uint256 slope;
        uint256 ts;
        uint256 blk;
    }

    event AddType(string name, int128 type_id);

    event NewTypeWeight(int128 indexed type_id, uint256 time, uint256 weight, uint256 total_weight);

    event NewGaugeWeight(address indexed gauge_address, uint256 time, uint256 weight, uint256 total_weight);

    event VoteForGauge(address indexed user, address indexed gauge_address, uint256 time, uint256 weight);

    event NewGauge(address indexed gauge_address, int128 gauge_type, uint256 weight);

    /**
     * @notice Get gauge type for address
     *  @param _addr Gauge address
     * @return Gauge type id
     */
    function gaugeTypes(address _addr) external view returns (int128);

    /**
     * @notice Add gauge `addr` of type `gauge_type` with weight `weight`
     * @param addr Gauge address
     * @param gaugeType Gauge type
     * @param weight Gauge weight
     */
    function addGauge(address addr, int128 gaugeType, uint256 weight) external;

    /**
     * @notice Checkpoint to fill data common for all gauges
     */
    function checkpoint() external;

    /**
     * @notice Checkpoint to fill data for both a specific gauge and common for all gauge
     * @param addr Gauge address
     */
    function checkpointGauge(address addr) external;

    /**
     * @notice Get Gauge relative weight (not more than 1.0) normalized to 1e18(e.g. 1.0 == 1e18). Inflation which will be received by
     * it is inflation_rate * relative_weight / 1e18
     * @param gaugeAddress Gauge address
     * @param time Relative weight at the specified timestamp in the past or present
     * @return Value of relative weight normalized to 1e18
     */
    function gaugeRelativeWeight(address gaugeAddress, uint256 time) external view returns (uint256);

    /**
     *  @notice Get gauge weight normalized to 1e18 and also fill all the unfilled values for type and gauge records
     * @dev Any address can call, however nothing is recorded if the values are filled already
     * @param gaugeAddress Gauge address
     * @param time Relative weight at the specified timestamp in the past or present
     * @return Value of relative weight normalized to 1e18
     */
    function gaugeRelativeWeightWrite(address gaugeAddress, uint256 time) external returns (uint256);

    /**
     * @notice Add gauge type with name `_name` and weight `weight`
     * @dev only owner call
     * @param _name Name of gauge type
     * @param weight Weight of gauge type
     */
    function addType(string memory _name, uint256 weight) external;

    /**
     * @notice Change gauge type `type_id` weight to `weight`
     * @dev only owner call
     * @param type_id Gauge type id
     * @param weight New Gauge weight
     */
    function changeTypeWeight(int128 type_id, uint256 weight) external;

    /**
     * @notice Change weight of gauge `addr` to `weight`
     * @param gaugeAddress `Gauge` contract address
     * @param weight New Gauge weight
     */
    function changeGaugeWeight(address gaugeAddress, uint256 weight) external;

    /**
     * @notice Allocate voting power for changing pool weights
     * @param gaugeAddress Gauge which `msg.sender` votes for
     * @param userWeight Weight for a gauge in bps (units of 0.01%). Minimal is 0.01%. Ignored if 0.
     *        example: 10%=1000,3%=300,0.01%=1,100%=10000
     */
    function voteForGaugeWeights(address gaugeAddress, uint256 userWeight) external;

    /**
     * @notice Get current gauge weight
     * @param addr Gauge address
     * @return Gauge weight
     */

    function getGaugeWeight(address addr) external view returns (uint256);

    /**
     * @notice Get current type weight
     * @param type_id Type id
     * @return Type weight
     */
    function getTypeWeight(int128 type_id) external view returns (uint256);

    /**
     * @notice Get current total (type-weighted) weight
     * @return Total weight
     */
    function getTotalWeight() external view returns (uint256);

    /**
     * @notice Get sum of gauge weights per type
     * @param type_id Type id
     * @return Sum of gauge weights
     */
    function getWeightsSumPreType(int128 type_id) external view returns (uint256);

    function votingEscrow() external view returns (address);
}

File 11 of 18 : ILT.sol
// SPDX-License-Identifier: LGPL-3.0

pragma solidity 0.8.17;

interface ILT {
    /**
     * @dev Emitted when LT inflation rate update
     *
     * Note once a year
     */
    event UpdateMiningParameters(uint256 time, uint256 rate, uint256 supply);

    /**
     * @dev Emitted when set LT minter,can set the minter only once, at creation
     */
    event SetMinter(address indexed minter);

    function rate() external view returns (uint256);

    /**
     * @notice Update mining rate and supply at the start of the epoch
     * @dev   Callable by any address, but only once per epoch
     *        Total supply becomes slightly larger if this function is called late
     */
    function updateMiningParameters() external;

    /**
     * @notice Get timestamp of the next mining epoch start while simultaneously updating mining parameters
     * @return Timestamp of the next epoch
     */
    function futureEpochTimeWrite() external returns (uint256);

    /**
     * @notice Current number of tokens in existence (claimed or unclaimed)
     */
    function availableSupply() external view returns (uint256);

    /**
     * @notice How much supply is mintable from start timestamp till end timestamp
     * @param start Start of the time interval (timestamp)
     * @param end End of the time interval (timestamp)
     * @return Tokens mintable from `start` till `end`
     */
    function mintableInTimeframe(uint256 start, uint256 end) external view returns (uint256);

    /**
     *  @notice Set the minter address
     *  @dev Only callable once, when minter has not yet been set
     *  @param _minter Address of the minter
     */
    function setMinter(address _minter) external;

    /**
     *  @notice Mint `value` tokens and assign them to `to`
     *   @dev Emits a Transfer event originating from 0x00
     *   @param to The account that will receive the created tokens
     *   @param value The amount that will be created
     *   @return bool success
     */
    function mint(address to, uint256 value) external returns (bool);

    /**
     * @notice Burn `value` tokens belonging to `msg.sender`
     * @dev Emits a Transfer event with a destination of 0x00
     * @param value The amount that will be burned
     * @return bool success
     */
    function burn(uint256 value) external returns (bool);
}

File 12 of 18 : IMinter.sol
// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.17;

interface IMinter {
    function token() external view returns (address);

    function controller() external view returns (address);

    function minted(address user, address gauge) external view returns (uint256);

    function mint(address gaugeAddress) external;
}

File 13 of 18 : IStaking.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

interface IStaking {
    event Staking(address indexed user, uint256 amount);
    event Unstaking(address indexed user, uint256 amount);
    event Redeem(address indexed user, uint256 amount);
    event RewardsAccrued(address user, uint256 amount);
    event RewardsClaimed(address indexed user, uint256 amount);

    function staking(uint256 amount, uint256 nonce, uint256 deadline, bytes memory signature) external returns (bool);

    function redeemAll() external returns (uint256);
}

File 14 of 18 : IVotingEscrow.sol
// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.17;

interface IVotingEscrow {
    struct Point {
        int256 bias;
        int256 slope;
        uint256 ts;
        uint256 blk;
    }

    struct LockedBalance {
        int256 amount;
        uint256 end;
    }

    event Deposit(
        address indexed provider,
        address indexed beneficiary,
        uint256 value,
        uint256 afterAmount,
        uint256 indexed locktime,
        uint256 _type,
        uint256 ts
    );
    event Withdraw(address indexed provider, uint256 value, uint256 ts);

    event Supply(uint256 prevSupply, uint256 supply);

    event SetSmartWalletChecker(address sender, address indexed newChecker, address oldChecker);

    event SetPermit2Address(address oldAddress, address newAddress);

    /***
     * @dev Get the most recently recorded rate of voting power decrease for `_addr`
     * @param _addr Address of the user wallet
     * @return Value of the slope
     */
    function getLastUserSlope(address _addr) external view returns (int256);

    /***
     * @dev Get the timestamp for checkpoint `_idx` for `_addr`
     * @param _addr User wallet address
     * @param _idx User epoch number
     * @return Epoch time of the checkpoint
     */
    function userPointHistoryTs(address _addr, uint256 _idx) external view returns (uint256);

    /***
     * @dev Get timestamp when `_addr`'s lock finishes
     * @param _addr User wallet
     * @return Epoch time of the lock end
     */
    function lockedEnd(address _addr) external view returns (uint256);

    function createLock(uint256 _value, uint256 _unlockTime, uint256 nonce, uint256 deadline, bytes memory signature) external;

    function createLockFor(
        address _beneficiary,
        uint256 _value,
        uint256 _unlockTime,
        uint256 nonce,
        uint256 deadline,
        bytes memory signature
    ) external;

    function increaseAmount(uint256 _value, uint256 nonce, uint256 deadline, bytes memory signature) external;

    function increaseAmountFor(address _beneficiary, uint256 _value, uint256 nonce, uint256 deadline, bytes memory signature) external;

    function increaseUnlockTime(uint256 _unlockTime) external;

    function checkpointSupply() external;

    function withdraw() external;

    function epoch() external view returns (uint256);

    function getUserPointHistory(address _userAddress, uint256 _index) external view returns (Point memory);

    function supplyPointHistory(uint256 _index) external view returns (int256 bias, int256 slope, uint256 ts, uint256 blk);

    /***
     * @notice Get the current voting power for `msg.sender`
     * @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
     * @param _addr User wallet address
     * @param _t Epoch time to return voting power at
     * @return User voting power
     * @dev return the present voting power if _t is 0
     */
    function balanceOfAtTime(address _addr, uint256 _t) external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function totalSupplyAtTime(uint256 _t) external view returns (uint256);

    function userPointEpoch(address _user) external view returns (uint256);
}

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

pragma solidity 0.8.17;

/**
 * @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 16 of 18 : IPermit2.sol
// SPDX-License-Identifier: LGPL-3.0

pragma solidity 0.8.17;

interface IPermit2 {
    /// @notice The token and amount details for a transfer signed in the permit transfer signature
    struct TokenPermissions {
        // ERC20 token address
        address token;
        // the maximum amount that can be spent
        uint256 amount;
    }

    /// @notice The signed permit message for a single token transfer
    struct PermitTransferFrom {
        TokenPermissions permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice Specifies the recipient address and amount for batched transfers.
    /// @dev Recipients and amounts correspond to the index of the signed token permissions array.
    /// @dev Reverts if the requested amount is greater than the permitted signed amount.
    struct SignatureTransferDetails {
        // recipient address
        address to;
        // spender requested amount
        uint256 requestedAmount;
    }

    /// @notice Used to reconstruct the signed permit message for multiple token transfers
    /// @dev Do not need to pass in spender address as it is required that it is msg.sender
    /// @dev Note that a user still signs over a spender address
    struct PermitBatchTransferFrom {
        // the tokens and corresponding amounts permitted for a transfer
        TokenPermissions[] permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
    /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
    /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
    /// @dev It returns a uint256 bitmap
    /// @dev The index, or wordPosition is capped at type(uint248).max
    function nonceBitmap(address, uint256) external view returns (uint256);

    /// @notice Transfers a token using a signed permit message
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Transfers a token using a signed permit message
    /// @notice Includes extra data provided by the caller to verify signature over
    /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param witness Extra data to include when checking the user signature
    /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
    /// @param signature The signature to verify
    function permitWitnessTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes32 witness,
        string calldata witnessTypeString,
        bytes calldata signature
    ) external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
    /// @notice Includes extra data provided by the caller to verify signature over
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param witness Extra data to include when checking the user signature
    /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
    /// @param signature The signature to verify
    function permitWitnessTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes32 witness,
        string calldata witnessTypeString,
        bytes calldata signature
    ) external;

    /// @notice Invalidates the bits specified in mask for the bitmap at the word position
    /// @dev The wordPos is maxed at type(uint248).max
    /// @param wordPos A number to index the nonceBitmap at
    /// @param mask A bitmap masked against msg.sender's current bitmap at the word position
    function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}

File 17 of 18 : LibTime.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.17;

library LibTime {

    // 7 * 86400 seconds - all future times are rounded by week
    uint256 public constant DAY = 86400;
    uint256 public constant WEEK = DAY * 7;

    /**
     * @dev times are rounded by week
     * @param time time
     */
    function timesRoundedByWeek(uint256 time) internal pure returns (uint256) {
        return (time / WEEK) * WEEK;
    }
}

File 18 of 18 : TransferHelper.sol
// SPDX-License-Identifier: LGPL-3.0

pragma solidity 0.8.17;

import "./IERC20.sol";
import "./IPermit2.sol";

library TransferHelper {
    
    /**
     * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case.
     *      This will revert due to insufficient balance or insufficient allowance.
     *      This function returns the actual amount received,
     *      which may be less than `amount` if there is a fee attached to the transfer.
     *
     *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
     */
    function doTransferFrom(address tokenAddress, address from, address to, uint256 amount) internal returns(uint256) {
        IERC20 token = IERC20(tokenAddress);
        uint256 balanceBefore = token.balanceOf(to);
        safeTransferFrom(token, from, to, amount);
        uint256 balanceAfter = token.balanceOf(to);
        uint256 actualAmount = balanceAfter - balanceBefore;
        assert(actualAmount <= amount);
        return actualAmount;
    }

    /**
     * @dev transfer with permit2
     */
    function doTransferIn(
        address permit2Address,
        address tokenAddress,
        uint256 _value,
        address from,
        uint256 nonce,
        uint256 deadline,
        bytes memory signature
    ) internal returns (uint256) {
        IPermit2.PermitTransferFrom memory permit = IPermit2.PermitTransferFrom({
            permitted: IPermit2.TokenPermissions({token: tokenAddress, amount: _value}),
            nonce: nonce,
            deadline: deadline
        });
        IPermit2.SignatureTransferDetails memory transferDetails = IPermit2.SignatureTransferDetails({
            to: address(this),
            requestedAmount: _value
        });
        // Read from storage once
        IERC20 token = IERC20(permit.permitted.token);
        uint256 balanceBefore = token.balanceOf(transferDetails.to);
        if (nonce == 0 && deadline == 0) {
            safeTransferFrom(token, from, transferDetails.to, transferDetails.requestedAmount);
        } else {
            IPermit2(permit2Address).permitTransferFrom(permit, transferDetails, from, signature);
        }
        // Calculate the amount that was *actually* transferred
        uint256 balanceAfter = IERC20(permit.permitted.token).balanceOf(address(this));
        uint256 actualAmount = balanceAfter - balanceBefore;
        assert(actualAmount <= transferDetails.requestedAmount);
        
        return actualAmount;
    }

    /**
     * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory
     *      error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to
     *      insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified
     *      it is >= amount, this should not revert in normal conditions.
     *
     *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
     */
    function doTransferOut(address tokenAddress, address to, uint256 amount) internal returns(uint256) {
        IERC20 token = IERC20(tokenAddress);
        uint256 balanceBefore = token.balanceOf(to);
        safeTransfer(token, to, amount);
        uint256 balanceAfter = token.balanceOf(to);
        uint256 actualAmount = balanceAfter - balanceBefore;
        assert(actualAmount <= amount);
        return actualAmount;
    }

    function doApprove(address tokenAddress, address to, uint256 amount) internal {
        IERC20 token = IERC20(tokenAddress);
        safeApprove(token, to, amount);
    }

    function safeTransferFrom(
        IERC20 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), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

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

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        IERC20 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), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

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

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        IERC20 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), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

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

        require(success, "APPROVE_FAILED");
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "viaIR": true,
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_stakedToken","type":"address"},{"internalType":"address","name":"_minter","type":"address"},{"internalType":"address","name":"_permit2Address","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsAccrued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"SetPermit2Address","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staking","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":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstaking","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"originalBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"originalSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"workingBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"workingSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"votingBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"votingTotal","type":"uint256"}],"name":"UpdateLiquidityLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","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":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"claimableTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"contract IGaugeController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"futureEpochTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"inflationRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"integrateCheckpoint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"integrateCheckpointOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"integrateFraction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"integrateInvSupplyOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isKilled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"kick","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"lpBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ltToken","outputs":[{"internalType":"contract ILT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minter","outputs":[{"internalType":"contract IMinter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"period","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"periodTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permit2Address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeemAll","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxIndex","type":"uint256"}],"name":"redeemByMaxIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isKilled","type":"bool"}],"name":"setKilled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAddress","type":"address"}],"name":"setPermit2Address","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"staking","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalNotRedeemAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"unstakedBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unstakedTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unstaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"unstakingBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"unstakingDayHistory","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unstakingMap","outputs":[{"internalType":"uint256","name":"notRedeemAmount","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unstakingTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"userCheckpoint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"votingEscrow","outputs":[{"internalType":"contract IVotingEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"workingBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"workingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

608060409080825234620006db57606081620032de8038038091620000258285620006fc565b833981010312620006db576200003b8162000720565b9060209062000059846200005184840162000720565b920162000720565b928451916200006883620006e0565b600b83526a7374616b656420484f504560a81b848401528551926200008d84620006e0565b60068452657374484f504560d01b8585015280516001600160401b0390818111620005db576003908154906001948583811c93168015620006d0575b8a841014620006ba578190601f9384811162000664575b508a90848311600114620005fd57600092620005f1575b505060001982851b1c191690851b1782555b8651928311620005db5760049687548581811c91168015620005d0575b8a821014620005bb57908183869594931162000561575b5089918411600114620004f657600093620004ea575b505082841b92600019911b1c19161784555b62000170336200076a565b6001600160a01b03928316918390620001a5841515620001908162000735565b6200019f848b16151562000735565b62000735565b1690620001b482151562000735565b620001c133151562000735565b60165460ff8116620004905760ff191617601655620001e0336200076a565b600780546001600160a01b03199081169093179055600880548316821790558651637e062a3560e11b81529085828681845afa908115620004855784879287946000916200043f575b501684600954161760095588519283809263f77c479160e01b82525afa8015620003f1578386918693600091620003fc575b50168084600a541617600a55885192838092634f2bfe5b60e01b82525afa8015620003f1578391600091620003b2575b501690600b541617600b55600080526010835242856000205560095416908451631627391760e11b815283818381865afa908115620003a75760009162000373575b50601555845163277dbafb60e01b815292918291849182906000905af1908115620003685760009162000333575b50600c555060168054610100600160a81b03191660089290921b610100600160a81b031691909117905551612b1f9081620007bf8239f35b82813d831162000360575b6200034a8183620006fc565b810103126200035d5750518038620002fb565b80fd5b503d6200033e565b84513d6000823e3d90fd5b908482813d83116200039f575b6200038c8183620006fc565b810103126200035d5750516000620002cd565b503d62000380565b86513d6000823e3d90fd5b91508582813d8311620003e9575b620003cc8183620006fc565b810103126200035d5750620003e2839162000720565b386200028b565b503d620003c0565b87513d6000823e3d90fd5b935091905082813d831162000437575b620004188183620006fc565b810103126200035d5750848362000430869362000720565b386200025b565b503d6200040c565b9384919395508092503d83116200047d575b6200045d8183620006fc565b810103126200035d575084918462000476889362000720565b3862000229565b503d62000451565b88513d6000823e3d90fd5b885162461bcd60e51b8152808701889052602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608490fd5b01519150388062000153565b9190859450601f19841692896000528a6000209360005b8c8282106200054a57505085116200052f575b50505050811b01845562000165565b01519060f884600019921b161c191690553880808062000520565b83850151875589989096019593840193016200050d565b909192935088600052896000208380870160051c8201928c8810620005b1575b9188918897969594930160051c01915b828110620005a15750506200013d565b6000815587965088910162000591565b9250819262000581565b602289634e487b7160e01b6000525260246000fd5b90607f169062000126565b634e487b7160e01b600052604160045260246000fd5b015190503880620000f7565b60008681528c8120899550929190601f198516908e5b8282106200064c575050841162000633575b505050811b01825562000109565b015160001983871b60f8161c1916905538808062000625565b8385015186558b979095019493840193018e62000613565b909150846000528a6000208480850160051c8201928d8610620006b0575b918991869594930160051c01915b828110620006a0575050620000e0565b6000815585945089910162000690565b9250819262000682565b634e487b7160e01b600052602260045260246000fd5b92607f1692620000c9565b600080fd5b604081019081106001600160401b03821117620005db57604052565b601f909101601f19168101906001600160401b03821190821017620005db57604052565b51906001600160a01b0382168203620006db57565b156200073d57565b60405162461bcd60e51b8152602060048201526005602482015264043453030360dc1b6044820152606490fd5b600680546001600160a01b0319908116909155600580549182166001600160a01b0393841690811790915591167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a356fe608080604052600436101561001357600080fd5b600090813560e01c90816306fdde0314611ae1575080630754617214611ab85780630959950414611a71578063095ea7b314611a4a57806317a3c37014611a2357806318160ddd14611a0557806323b872dd146119155780632f4350c214611805578063313ce567146117e957806331e202cc146117bf57806331f9e35b146117a157806339509351146117515780634f2bfe5b14611728578063505362c4146116ff57806353f7425f146116bc5780635fcbd2851461169357806366be23221461167157806370a0823114611639578063715018a6146115d257806373dd0555146111095780637699b4cd146110d05780637915b1c3146110a657806379ba509714610fdd5780637b4dacef14610fc257806384d2422614610ef55780638b45a67314610ed15780638da5cb5b14610ea85780638fe8a10114610e8257806390827da714610ce1578063958da8de14610ca857806395d89b4114610b9757806396c55175146109915780639c46665c146109655780639e48d35e14610947578063a154f1bb1461087c578063a457c2d7146107d7578063a5870d6b146107ae578063a9059cbb14610743578063b07b709b1461069d578063b1bd608614610664578063bdc83b1f14610646578063c522498314610619578063c826860414610579578063c8562f721461055b578063caa0b9ed14610522578063dd62ed3e146104d3578063e30c3978146104aa578063ef78d4fd1461048c578063f2fde38b1461041e578063f77c4791146103f55763fc566d521461025257600080fd5b346103f257602090816003193601126103f25760043561027b61027433611f34565b1515611e18565b8161028533612464565b338352601884526040832090839160019384820195600291828401965b885481116103e4578187101561034557808352878a52600160408420015442101580610334575b6102dc575b6102d790611e8c565b6102a2565b9561032c6102d7916102f360408620548099611d09565b978986528a8d52866040872001600160ff19825416179055610316818954611e9b565b88556103256017918254611e9b565b9055611e8c565b9690506102ce565b5060ff8460408520015416156102c9565b5050509350509250505b8161035e575b50604051908152f35b6103a49061036c8333611fc3565b60075461038590849033906001600160a01b0316612986565b5061039d610392336120ca565b915460175490611e9b565b9033612265565b6103b081601a54611e9b565b601a556040518181527f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a6833392a238610355565b50505093505092505061034f565b80fd5b50346103f257806003193601126103f257600a546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f257610438611c01565b610440611cb1565b600680546001600160a01b0319166001600160a01b039283169081179091556005549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b50346103f257806003193601126103f2576020600f54604051908152f35b50346103f257806003193601126103f2576006546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576104ed611c01565b60406104f7611c17565b9260018060a01b03809316815260016020522091166000526020526020604060002054604051908152f35b50346103f25760203660031901126103f2576020906040906001600160a01b0361054a611c01565b168152601483522054604051908152f35b50346103f257806003193601126103f2576020600e54604051908152f35b50346103f257602090816003193601126103f257610595611c01565b6001600160a01b031681526018825260408120600181810154600292830192849291805b6105c8575b8686604051908152f35b80845284875260ff8260408620015416610614578260408520015442106105f9575b6105f390611e7f565b806105b9565b9461060c6105f391604086205490611d09565b9590506105ea565b6105be565b50346103f257806003193601126103f25760165460405160089190911c6001600160a01b03168152602090f35b50346103f257806003193601126103f2576020601754604051908152f35b50346103f25760203660031901126103f2576020906040906001600160a01b0361068c611c01565b168152601283522054604051908152f35b50346103f25760203660031901126103f2576106b7611c01565b6001600160a01b038181163314908115610735575b501561070857806106df6106fd92612464565b6106e8816120ca565b6106f760025460175490611e9b565b91612265565b602060405160018152f35b60405162461bcd60e51b8152602060048201526005602482015264047503030360dc1b6044820152606490fd5b9050600854163314826106cc565b50346103f25760403660031901126103f2576106fd610760611c01565b61079360243561077a81610773336120ca565b1015611e18565b61078333612464565b61078c83612464565b82336120f7565b6106df61079f336120ca565b61039d60025460175490611e9b565b50346103f257806003193601126103f2576009546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576107f1611c01565b60406024359233815260016020522060018060a01b03821660005260205260406000205491808310610829576106fd92039033611d16565b60405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608490fd5b50346103f25760203660031901126103f257610896611c01565b61089e611cb1565b6001600160a01b038181161561091a5760168054610100600160a81b03198116600885811b610100600160a81b031691909117909255604080516001600160a01b039290931c939093168116825290921660208301527f35f7c25f313d8b378045946ab43056cd8fbf3cb403496ebaa0beef117936ec3291a180f35b60405162461bcd60e51b8152602060048201526005602482015264043453030360dc1b6044820152606490fd5b50346103f257806003193601126103f2576020600c54604051908152f35b50346103f25760203660031901126103f2576020610989610984611c01565b6120ca565b604051908152f35b50346103f257602080600319360112610b93576109ac611c01565b9060018060a01b038083169182855260138152604085205491600b5416926040516381fc83bb60e01b81528160048201528281602481885afa8015610b595783908890610b64575b60405163eac6a66760e01b81526001600160a01b0389166004820152602481019190915291508180604481015b0381885afa908115610b59578791610b2c575b50610a3e866120ca565b604051633037408d60e01b81526001600160a01b038816600482015242602482015290958490829060449082905afa908115610b21578891610aec575b5090600d94610a949215918215610ae2575b5050612935565b855252604083205490602881029080820460281490151715610ace57610acb92916064610ac2920410612935565b6106df81612464565b80f35b634e487b7160e01b84526011600452602484fd5b1190503880610a8d565b9190508382813d8311610b1a575b610b048183611c8f565b81010312610b15579051600d610a7b565b600080fd5b503d610afa565b6040513d8a823e3d90fd5b90508281813d8311610b52575b610b438183611c8f565b81010312610b15575138610a34565b503d610b39565b6040513d89823e3d90fd5b5081813d8311610b8c575b610b798183611c8f565b81010312610b155782610a2191516109f4565b503d610b6f565b5080fd5b50346103f257806003193601126103f257604051908060045491600183811c92818516948515610c9e575b6020958686108114610c8a57858852879493929187908215610c68575050600114610c0e575b5050610bf692500383611c8f565b610c0a604051928284938452830190611bc1565b0390f35b90859250600482527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b858310610c50575050610bf693508201013880610be8565b80548389018501528794508693909201918101610c38565b9250935050610bf694915060ff191682840152151560051b8201013880610be8565b634e487b7160e01b83526022600452602483fd5b93607f1693610bc2565b50346103f25760203660031901126103f2576020906040906001600160a01b03610cd0611c01565b168152600d83522054604051908152f35b50346103f257602080600319360112610b935760043590610d03821515611e18565b610d1082610773336120ca565b610d1933612464565b62015180804201804211610e6e57819004818102918183041490151715610ace576224ea00810190818111610e585780855260198352610d5d846040872054611d09565b908552601983526040852055610d7583601a54611d09565b601a553384526018825260408420610d8e848254611d09565b8155600181019182549260018401809411610e445791600291847ff2619dcba9802bb8ec071016f659320c48304701ba220f0420bed16f87139a66969594558260405192610ddb84611c5f565b88845286840192835260408401958a87528a520185526040882091518255516001820155019051151560ff80198354169116179055610e38610e1f84601754611d09565b8060175561039d610e2f336120ca565b91600254611e9b565b6040519283523392a280f35b634e487b7160e01b87526011600452602487fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b85526011600452602485fd5b50346103f257806003193601126103f257602060ff60065460a01c166040519015158152f35b50346103f257806003193601126103f2576005546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f2576020610989610ef0611c01565b611f34565b50346103f257602090816003193601126103f257610f6a90610f15611c01565b610f1e81612464565b6001600160a01b039081168083526014855260408084205460085491516308b752bb60e41b81526004810193909352306024840152919491928692869290911690829081906044820190565b03915afa918215610fb65791610f85575b6109899250611e9b565b90508282813d8311610faf575b610f9c8183611c8f565b81010312610b1557610989915190610f7b565b503d610f92565b604051903d90823e3d90fd5b50346103f257806003193601126103f2576020610989611ea8565b50346103f257806003193601126103f2576006546001600160a01b03338183160361104f576bffffffffffffffffffffffff60a01b8092166006556005549133908316176005553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b60405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608490fd5b50346103f25760203660031901126103f25760406020916004358152601983522054604051908152f35b50346103f25760203660031901126103f2576020906040906001600160a01b036110f8611c01565b168152601383522054604051908152f35b50346103f25760803660031901126103f25767ffffffffffffffff6064358181116115ba57366023820112156115ba5780600401359182116115be5782906040519261115f6020601f19601f8401160185611c8f565b80845236602482840101116115ba5780602460209301838601378301015261118a6004351515611e18565b6007546040516370a0823160e01b81523360048201526001600160a01b039091169190602081602481865afa801561143a578490611582575b6111d291506004351115611e18565b60018060a01b0360165460081c1690604051926111ee84611c2d565b835260043560208401526040519261120584611c5f565b8352602435602084015260443560408401526040519161122483611c2d565b308352600435602084015260018060a01b038451511690604051926370a0823160e01b8452306004850152602084602481865afa938415610b5957879461154a575b506024351580611540575b1561148157505082516020808501516040516323b872dd60e01b81523360048201526001600160a01b039093166024840152604483015291869160649183905af13d15601f3d116001875114161716156114455760206024935b51516040516370a0823160e01b815230600482015294859182906001600160a01b03165afa92831561143a578493611404575b5061130e60209161131894611e9b565b9101511015612969565b61132133612464565b33156113bf57611335600435600254611d09565b600255338152806020526040812060043581540190556040519060043582527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203393a361138661079f336120ca565b60405160043581527fb831f69f1cebc12b23cd864ce5bfea2669d01956050a0147d71d418074559c2160203392a2602060405160018152f35b60405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606490fd5b92506020833d602011611432575b8161141f60209383611c8f565b81010312610b155791519161130e6112fe565b3d9150611412565b6040513d86823e3d90fd5b60405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606490fd5b908092509594953b1561153c5760405163187945bd60e11b8152865180516001600160a01b0316600483015260200151602482015291859183919082908490829061150c9060208d810151604485015260408e015160648501528b516001600160a01b031660848501528b015160a48401523360c484015261010060e4840152610104830190611bc1565b03925af1801561143a57611527575b506020602493946112cb565b602493611535602092611c7b565b935061151b565b8480fd5b5060443515611271565b9093506020813d60201161157a575b8161156660209383611c8f565b8101031261157657519238611266565b8680fd5b3d9150611559565b506020813d6020116115b2575b8161159c60209383611c8f565b810103126115ae576111d290516111c3565b8380fd5b3d915061158f565b8280fd5b634e487b7160e01b83526041600452602483fd5b50346103f257806003193601126103f2576115eb611cb1565b600680546001600160a01b031990811690915560058054918216905581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346103f25760203660031901126103f2576020906040906001600160a01b03611661611c01565b1681528083522054604051908152f35b50346103f257806003193601126103f257602061098960025460175490611e9b565b50346103f257806003193601126103f2576007546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760409081906001600160a01b036116e3611c01565b1681526018602052206001815491015482519182526020820152f35b50346103f257806003193601126103f2576040602091600f548152601083522054604051908152f35b50346103f257806003193601126103f257600b546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576106fd9061179a611772611c01565b9133815260016020526040812060018060a01b03841682526020526040602435912054611d09565b9033611d16565b50346103f257806003193601126103f2576020601554604051908152f35b50346103f25760203660031901126103f25760406020916004358152601083522054604051908152f35b50346103f257806003193601126103f257602060405160128152f35b50346103f257806003193601126103f25761181f33611f34565b61182a811515611e18565b61183333612464565b3382526020916018835260408120600191828201549160029383858301945b6118a9575b87876118678861036c8333611fc3565b61187381601a54611e9b565b601a556040518181527f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a6833392a2604051908152f35b80845284885260ff8660408620015416611910576118d490826040862001544210156118da57611e7f565b80611852565b60408520548760408720018460ff198254161790556118fa818654611e9b565b85556119096017918254611e9b565b9055611e7f565b611857565b50346103f25760603660031901126103f25761192f611c01565b90611938611c17565b9060406044359161194c83610773876120ca565b61195585612464565b61195e84612464565b6001600160a01b038516815260016020818152838320338452905291902054908101611998575b50826106df6106df92846106fd966120f7565b928184106119c0576106df826119b76106df946106fd97033385611d16565b92505092611985565b60405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b50346103f257806003193601126103f2576020600254604051908152f35b50346103f257806003193601126103f2576020610989601a54611a44611ea8565b90611e9b565b50346103f25760403660031901126103f2576106fd611a67611c01565b6024359033611d16565b50346103f25760203660031901126103f257600435801515809103610b9357611a98611cb1565b6006805460ff60a01b191660a09290921b60ff60a01b1691909117905580f35b50346103f257806003193601126103f2576008546040516001600160a01b039091168152602090f35b82346103f257806003193601126103f2578060035491600183811c92818516948515611bb7575b6020958686108114610c8a57858852879493929187908215611b95575050600114611b3b575050610bf692500383611c8f565b90859250600382527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b858310611b7d575050610bf693508201018580610be8565b80548389018501528794508693909201918101611b65565b9250935050610bf694915060ff191682840152151560051b8201018580610be8565b93607f1693611b08565b919082519283825260005b848110611bed575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201611bcc565b600435906001600160a01b0382168203610b1557565b602435906001600160a01b0382168203610b1557565b6040810190811067ffffffffffffffff821117611c4957604052565b634e487b7160e01b600052604160045260246000fd5b6060810190811067ffffffffffffffff821117611c4957604052565b67ffffffffffffffff8111611c4957604052565b90601f8019910116810190811067ffffffffffffffff821117611c4957604052565b6005546001600160a01b03163303611cc557565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b91908201809211610e5857565b6001600160a01b03908116918215611dc75716918215611d775760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b15611e1f57565b60405162461bcd60e51b815260206004820152600560248201526421a298181960d91b6044820152606490fd5b8115611e56570490565b634e487b7160e01b600052601260045260246000fd5b81810292918115918404141715610e5857565b8015610e58576000190190565b6000198114610e585760010190565b91908203918211610e5857565b600062015180804201804211611f20578190049080820291808304821490151715611f20579082915b601c8310611edf5750505090565b9091928382028281048503610e5857611f1991611eff611f139286611e9b565b600052601960205260406000205490611d09565b93611e8c565b9190611ed1565b634e487b7160e01b83526011600452602483fd5b6001600160a01b031660009081526018602090815260408083206001818101549394936002928301939290805b611f70575b5050505050905090565b8060005284875260ff8285600020015416611fbe5782846000200154421015611fa3575b611f9d90611e7f565b80611f61565b94611fb6611f9d91856000205490611d09565b959050611f94565b611f66565b6001600160a01b0316801561207b5760009181835282602052604083205481811061202b57817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef926020928587528684520360408620558060025403600255604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608490fd5b60018060a01b031660005260006020526120f4604060002054601860205260406000205490611e9b565b90565b6001600160a01b0390811691821561221257169182156121c15760008281528060205260408120549180831061216d57604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815220818154019055604051908152a3565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b600b5460408051633037408d60e01b81526001600160a01b0384811660048301524260248301529283169695929493909260209283856044818c5afa94851561242857600095612433575b508360249697989982519788809263d2dcd93360e01b82524260048301525afa958615612428576000966123f9575b506028870287810460281488151715610e5857606490048661238a575b7f47211fe8b1eecabef1c013b28eb9caa892fd6bf2d3f1f5111d08cc115de841659860e0989796959493929180891015612384575087935b169687600052600d855261235d826000205485846000205561235886600e54611d09565b611e9b565b9485600e5582519889528801528601526060850152608084015260a083015260c0820152a1565b93612334565b9695949392919061239b8583611e6c565b98603c8a0299808b04603c1490151715610e58576123e960e09960646123e28a7f47211fe8b1eecabef1c013b28eb9caa892fd6bf2d3f1f5111d08cc115de841659e611e4c565b0490611d09565b91929394959697985098506122fc565b90958482813d8311612421575b6124108183611c8f565b810103126103f257505194386122df565b503d612406565b50513d6000823e3d90fd5b9894848a813d831161245d575b61244a8183611c8f565b810103126103f2575097519397836122b0565b503d612440565b60405160009160c0820167ffffffffffffffff811183821017612921576040528260208301528260408301528260608301528260808301528260a0830152600f548083528352601060205260408320546020830152815183526011602052604083205460408301526015548060608401526080830152600c548060a08401526020830151111561283b575b600a546001600160a01b0316803b156115ae57838091602460405180948193638aca6a2360e01b83523060048401525af1801561143a57612828575b5060ff60065460a01c1661281d575b60208201518042116125ef575b50815160018101809111610ace579081604092845280600f55845260106020524282852055818301518351855260116020528285205560018060a01b031691828452600d602052670de0b6b3a76400006125ba838620546125b48585015160126020528689205490611e9b565b90611e6c565b0483855260146020526125d1838620918254611d09565b90550151908252601260205260408220556013602052604042912055565b600e5462093a808201808311610e585762093a80900462093a8081029080820462093a801490151715610e585742811015612816575b600a546001600160a01b031692869291905b6101f4841061264a575b50505050612547565b6126548183611e9b565b9062093a808104918262093a8081020462093a801483151715610e5857604051630cb8c08d60e31b815230600482015262093a809390930260248401526020836044818a5afa92831561280b579189918694938c946127d0575b5084612701575b50505050504281146126fb578062093a8081018111610e58574262093a80820110156126f15762093a806126ea910193611e8c565b9290612637565b506126ea42611f13565b80612641565b60a0830151868382101591826127c6575b50501561279d57506127758361275f84604061275789612752612752986125b46127476127839d61278d9f9d60600151611e6c565b9160a0880151611e9b565b611e4c565b910151611d09565b60408d015260808c015160608d01819052611e6c565b6125b460a08c015187611e9b565b6040880151611d09565b60408701525b81388781806126b5565b61275291506127b76127839460606127bc97950151611e6c565b611e6c565b6040870152612793565b1090508638612712565b9250925092506020813d602011612803575b816127ef60209383611c8f565b81010312610b1557849289915192386126ae565b3d91506127e2565b6040513d8c823e3d90fd5b5042612625565b82606083015261253a565b61283490939193611c7b565b913861252b565b60095460405163277dbafb60e01b81526001600160a01b039160209082906004908290899087165af19081156129165785916128e3575b50600c55600954604051631627391760e11b81529160209183916004918391165afa90811561143a5784916128b1575b508060808401526015556124ef565b90506020813d6020116128db575b816128cc60209383611c8f565b810103126115ae5751386128a2565b3d91506128bf565b90506020813d60201161290e575b816128fe60209383611c8f565b8101031261153c57516004612872565b3d91506128f1565b6040513d87823e3d90fd5b634e487b7160e01b84526041600452602484fd5b1561293c57565b60405162461bcd60e51b8152602060048201526005602482015264475030303160d81b6044820152606490fd5b1561297057565b634e487b7160e01b600052600160045260246000fd5b604080516370a0823160e01b8082526001600160a01b038086166004840181905292959394169260209291908387602481885afa968715612ade57600097612aae575b5060006044859288519063a9059cbb60e01b825260048201528a602482015282885af13d15601f3d1160016000511416171615612a7857906024839286519586938492835260048301525afa928315612a6e5750600092612a3d575b50506120f491612a3491611e9b565b91821115612969565b81819392933d8311612a67575b612a548183611c8f565b810103126103f257505181612a34612a25565b503d612a4a565b513d6000823e3d90fd5b845162461bcd60e51b815260048101849052600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b90968482813d8311612ad7575b612ac58183611c8f565b810103126103f25750519560006129c9565b503d612abb565b86513d6000823e3d90fdfea26469706673582212206bed956b7effe049f0ab60543dc33361256b1d1e0807d7f2a3c83c8870d6113064736f6c63430008110033000000000000000000000000c353bf07405304aeab75f4c2fac7e88d6a68f98e00000000000000000000000094afb2c17af24cfacf19f364628f459dfab2688f000000000000000000000000c53c83d26151dbcffa349fae20b6155299e87a35

Deployed Bytecode

0x608080604052600436101561001357600080fd5b600090813560e01c90816306fdde0314611ae1575080630754617214611ab85780630959950414611a71578063095ea7b314611a4a57806317a3c37014611a2357806318160ddd14611a0557806323b872dd146119155780632f4350c214611805578063313ce567146117e957806331e202cc146117bf57806331f9e35b146117a157806339509351146117515780634f2bfe5b14611728578063505362c4146116ff57806353f7425f146116bc5780635fcbd2851461169357806366be23221461167157806370a0823114611639578063715018a6146115d257806373dd0555146111095780637699b4cd146110d05780637915b1c3146110a657806379ba509714610fdd5780637b4dacef14610fc257806384d2422614610ef55780638b45a67314610ed15780638da5cb5b14610ea85780638fe8a10114610e8257806390827da714610ce1578063958da8de14610ca857806395d89b4114610b9757806396c55175146109915780639c46665c146109655780639e48d35e14610947578063a154f1bb1461087c578063a457c2d7146107d7578063a5870d6b146107ae578063a9059cbb14610743578063b07b709b1461069d578063b1bd608614610664578063bdc83b1f14610646578063c522498314610619578063c826860414610579578063c8562f721461055b578063caa0b9ed14610522578063dd62ed3e146104d3578063e30c3978146104aa578063ef78d4fd1461048c578063f2fde38b1461041e578063f77c4791146103f55763fc566d521461025257600080fd5b346103f257602090816003193601126103f25760043561027b61027433611f34565b1515611e18565b8161028533612464565b338352601884526040832090839160019384820195600291828401965b885481116103e4578187101561034557808352878a52600160408420015442101580610334575b6102dc575b6102d790611e8c565b6102a2565b9561032c6102d7916102f360408620548099611d09565b978986528a8d52866040872001600160ff19825416179055610316818954611e9b565b88556103256017918254611e9b565b9055611e8c565b9690506102ce565b5060ff8460408520015416156102c9565b5050509350509250505b8161035e575b50604051908152f35b6103a49061036c8333611fc3565b60075461038590849033906001600160a01b0316612986565b5061039d610392336120ca565b915460175490611e9b565b9033612265565b6103b081601a54611e9b565b601a556040518181527f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a6833392a238610355565b50505093505092505061034f565b80fd5b50346103f257806003193601126103f257600a546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f257610438611c01565b610440611cb1565b600680546001600160a01b0319166001600160a01b039283169081179091556005549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b50346103f257806003193601126103f2576020600f54604051908152f35b50346103f257806003193601126103f2576006546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576104ed611c01565b60406104f7611c17565b9260018060a01b03809316815260016020522091166000526020526020604060002054604051908152f35b50346103f25760203660031901126103f2576020906040906001600160a01b0361054a611c01565b168152601483522054604051908152f35b50346103f257806003193601126103f2576020600e54604051908152f35b50346103f257602090816003193601126103f257610595611c01565b6001600160a01b031681526018825260408120600181810154600292830192849291805b6105c8575b8686604051908152f35b80845284875260ff8260408620015416610614578260408520015442106105f9575b6105f390611e7f565b806105b9565b9461060c6105f391604086205490611d09565b9590506105ea565b6105be565b50346103f257806003193601126103f25760165460405160089190911c6001600160a01b03168152602090f35b50346103f257806003193601126103f2576020601754604051908152f35b50346103f25760203660031901126103f2576020906040906001600160a01b0361068c611c01565b168152601283522054604051908152f35b50346103f25760203660031901126103f2576106b7611c01565b6001600160a01b038181163314908115610735575b501561070857806106df6106fd92612464565b6106e8816120ca565b6106f760025460175490611e9b565b91612265565b602060405160018152f35b60405162461bcd60e51b8152602060048201526005602482015264047503030360dc1b6044820152606490fd5b9050600854163314826106cc565b50346103f25760403660031901126103f2576106fd610760611c01565b61079360243561077a81610773336120ca565b1015611e18565b61078333612464565b61078c83612464565b82336120f7565b6106df61079f336120ca565b61039d60025460175490611e9b565b50346103f257806003193601126103f2576009546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576107f1611c01565b60406024359233815260016020522060018060a01b03821660005260205260406000205491808310610829576106fd92039033611d16565b60405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608490fd5b50346103f25760203660031901126103f257610896611c01565b61089e611cb1565b6001600160a01b038181161561091a5760168054610100600160a81b03198116600885811b610100600160a81b031691909117909255604080516001600160a01b039290931c939093168116825290921660208301527f35f7c25f313d8b378045946ab43056cd8fbf3cb403496ebaa0beef117936ec3291a180f35b60405162461bcd60e51b8152602060048201526005602482015264043453030360dc1b6044820152606490fd5b50346103f257806003193601126103f2576020600c54604051908152f35b50346103f25760203660031901126103f2576020610989610984611c01565b6120ca565b604051908152f35b50346103f257602080600319360112610b93576109ac611c01565b9060018060a01b038083169182855260138152604085205491600b5416926040516381fc83bb60e01b81528160048201528281602481885afa8015610b595783908890610b64575b60405163eac6a66760e01b81526001600160a01b0389166004820152602481019190915291508180604481015b0381885afa908115610b59578791610b2c575b50610a3e866120ca565b604051633037408d60e01b81526001600160a01b038816600482015242602482015290958490829060449082905afa908115610b21578891610aec575b5090600d94610a949215918215610ae2575b5050612935565b855252604083205490602881029080820460281490151715610ace57610acb92916064610ac2920410612935565b6106df81612464565b80f35b634e487b7160e01b84526011600452602484fd5b1190503880610a8d565b9190508382813d8311610b1a575b610b048183611c8f565b81010312610b15579051600d610a7b565b600080fd5b503d610afa565b6040513d8a823e3d90fd5b90508281813d8311610b52575b610b438183611c8f565b81010312610b15575138610a34565b503d610b39565b6040513d89823e3d90fd5b5081813d8311610b8c575b610b798183611c8f565b81010312610b155782610a2191516109f4565b503d610b6f565b5080fd5b50346103f257806003193601126103f257604051908060045491600183811c92818516948515610c9e575b6020958686108114610c8a57858852879493929187908215610c68575050600114610c0e575b5050610bf692500383611c8f565b610c0a604051928284938452830190611bc1565b0390f35b90859250600482527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b858310610c50575050610bf693508201013880610be8565b80548389018501528794508693909201918101610c38565b9250935050610bf694915060ff191682840152151560051b8201013880610be8565b634e487b7160e01b83526022600452602483fd5b93607f1693610bc2565b50346103f25760203660031901126103f2576020906040906001600160a01b03610cd0611c01565b168152600d83522054604051908152f35b50346103f257602080600319360112610b935760043590610d03821515611e18565b610d1082610773336120ca565b610d1933612464565b62015180804201804211610e6e57819004818102918183041490151715610ace576224ea00810190818111610e585780855260198352610d5d846040872054611d09565b908552601983526040852055610d7583601a54611d09565b601a553384526018825260408420610d8e848254611d09565b8155600181019182549260018401809411610e445791600291847ff2619dcba9802bb8ec071016f659320c48304701ba220f0420bed16f87139a66969594558260405192610ddb84611c5f565b88845286840192835260408401958a87528a520185526040882091518255516001820155019051151560ff80198354169116179055610e38610e1f84601754611d09565b8060175561039d610e2f336120ca565b91600254611e9b565b6040519283523392a280f35b634e487b7160e01b87526011600452602487fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b85526011600452602485fd5b50346103f257806003193601126103f257602060ff60065460a01c166040519015158152f35b50346103f257806003193601126103f2576005546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f2576020610989610ef0611c01565b611f34565b50346103f257602090816003193601126103f257610f6a90610f15611c01565b610f1e81612464565b6001600160a01b039081168083526014855260408084205460085491516308b752bb60e41b81526004810193909352306024840152919491928692869290911690829081906044820190565b03915afa918215610fb65791610f85575b6109899250611e9b565b90508282813d8311610faf575b610f9c8183611c8f565b81010312610b1557610989915190610f7b565b503d610f92565b604051903d90823e3d90fd5b50346103f257806003193601126103f2576020610989611ea8565b50346103f257806003193601126103f2576006546001600160a01b03338183160361104f576bffffffffffffffffffffffff60a01b8092166006556005549133908316176005553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b60405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608490fd5b50346103f25760203660031901126103f25760406020916004358152601983522054604051908152f35b50346103f25760203660031901126103f2576020906040906001600160a01b036110f8611c01565b168152601383522054604051908152f35b50346103f25760803660031901126103f25767ffffffffffffffff6064358181116115ba57366023820112156115ba5780600401359182116115be5782906040519261115f6020601f19601f8401160185611c8f565b80845236602482840101116115ba5780602460209301838601378301015261118a6004351515611e18565b6007546040516370a0823160e01b81523360048201526001600160a01b039091169190602081602481865afa801561143a578490611582575b6111d291506004351115611e18565b60018060a01b0360165460081c1690604051926111ee84611c2d565b835260043560208401526040519261120584611c5f565b8352602435602084015260443560408401526040519161122483611c2d565b308352600435602084015260018060a01b038451511690604051926370a0823160e01b8452306004850152602084602481865afa938415610b5957879461154a575b506024351580611540575b1561148157505082516020808501516040516323b872dd60e01b81523360048201526001600160a01b039093166024840152604483015291869160649183905af13d15601f3d116001875114161716156114455760206024935b51516040516370a0823160e01b815230600482015294859182906001600160a01b03165afa92831561143a578493611404575b5061130e60209161131894611e9b565b9101511015612969565b61132133612464565b33156113bf57611335600435600254611d09565b600255338152806020526040812060043581540190556040519060043582527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203393a361138661079f336120ca565b60405160043581527fb831f69f1cebc12b23cd864ce5bfea2669d01956050a0147d71d418074559c2160203392a2602060405160018152f35b60405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606490fd5b92506020833d602011611432575b8161141f60209383611c8f565b81010312610b155791519161130e6112fe565b3d9150611412565b6040513d86823e3d90fd5b60405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606490fd5b908092509594953b1561153c5760405163187945bd60e11b8152865180516001600160a01b0316600483015260200151602482015291859183919082908490829061150c9060208d810151604485015260408e015160648501528b516001600160a01b031660848501528b015160a48401523360c484015261010060e4840152610104830190611bc1565b03925af1801561143a57611527575b506020602493946112cb565b602493611535602092611c7b565b935061151b565b8480fd5b5060443515611271565b9093506020813d60201161157a575b8161156660209383611c8f565b8101031261157657519238611266565b8680fd5b3d9150611559565b506020813d6020116115b2575b8161159c60209383611c8f565b810103126115ae576111d290516111c3565b8380fd5b3d915061158f565b8280fd5b634e487b7160e01b83526041600452602483fd5b50346103f257806003193601126103f2576115eb611cb1565b600680546001600160a01b031990811690915560058054918216905581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346103f25760203660031901126103f2576020906040906001600160a01b03611661611c01565b1681528083522054604051908152f35b50346103f257806003193601126103f257602061098960025460175490611e9b565b50346103f257806003193601126103f2576007546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760409081906001600160a01b036116e3611c01565b1681526018602052206001815491015482519182526020820152f35b50346103f257806003193601126103f2576040602091600f548152601083522054604051908152f35b50346103f257806003193601126103f257600b546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576106fd9061179a611772611c01565b9133815260016020526040812060018060a01b03841682526020526040602435912054611d09565b9033611d16565b50346103f257806003193601126103f2576020601554604051908152f35b50346103f25760203660031901126103f25760406020916004358152601083522054604051908152f35b50346103f257806003193601126103f257602060405160128152f35b50346103f257806003193601126103f25761181f33611f34565b61182a811515611e18565b61183333612464565b3382526020916018835260408120600191828201549160029383858301945b6118a9575b87876118678861036c8333611fc3565b61187381601a54611e9b565b601a556040518181527f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a6833392a2604051908152f35b80845284885260ff8660408620015416611910576118d490826040862001544210156118da57611e7f565b80611852565b60408520548760408720018460ff198254161790556118fa818654611e9b565b85556119096017918254611e9b565b9055611e7f565b611857565b50346103f25760603660031901126103f25761192f611c01565b90611938611c17565b9060406044359161194c83610773876120ca565b61195585612464565b61195e84612464565b6001600160a01b038516815260016020818152838320338452905291902054908101611998575b50826106df6106df92846106fd966120f7565b928184106119c0576106df826119b76106df946106fd97033385611d16565b92505092611985565b60405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b50346103f257806003193601126103f2576020600254604051908152f35b50346103f257806003193601126103f2576020610989601a54611a44611ea8565b90611e9b565b50346103f25760403660031901126103f2576106fd611a67611c01565b6024359033611d16565b50346103f25760203660031901126103f257600435801515809103610b9357611a98611cb1565b6006805460ff60a01b191660a09290921b60ff60a01b1691909117905580f35b50346103f257806003193601126103f2576008546040516001600160a01b039091168152602090f35b82346103f257806003193601126103f2578060035491600183811c92818516948515611bb7575b6020958686108114610c8a57858852879493929187908215611b95575050600114611b3b575050610bf692500383611c8f565b90859250600382527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b858310611b7d575050610bf693508201018580610be8565b80548389018501528794508693909201918101611b65565b9250935050610bf694915060ff191682840152151560051b8201018580610be8565b93607f1693611b08565b919082519283825260005b848110611bed575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201611bcc565b600435906001600160a01b0382168203610b1557565b602435906001600160a01b0382168203610b1557565b6040810190811067ffffffffffffffff821117611c4957604052565b634e487b7160e01b600052604160045260246000fd5b6060810190811067ffffffffffffffff821117611c4957604052565b67ffffffffffffffff8111611c4957604052565b90601f8019910116810190811067ffffffffffffffff821117611c4957604052565b6005546001600160a01b03163303611cc557565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b91908201809211610e5857565b6001600160a01b03908116918215611dc75716918215611d775760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b15611e1f57565b60405162461bcd60e51b815260206004820152600560248201526421a298181960d91b6044820152606490fd5b8115611e56570490565b634e487b7160e01b600052601260045260246000fd5b81810292918115918404141715610e5857565b8015610e58576000190190565b6000198114610e585760010190565b91908203918211610e5857565b600062015180804201804211611f20578190049080820291808304821490151715611f20579082915b601c8310611edf5750505090565b9091928382028281048503610e5857611f1991611eff611f139286611e9b565b600052601960205260406000205490611d09565b93611e8c565b9190611ed1565b634e487b7160e01b83526011600452602483fd5b6001600160a01b031660009081526018602090815260408083206001818101549394936002928301939290805b611f70575b5050505050905090565b8060005284875260ff8285600020015416611fbe5782846000200154421015611fa3575b611f9d90611e7f565b80611f61565b94611fb6611f9d91856000205490611d09565b959050611f94565b611f66565b6001600160a01b0316801561207b5760009181835282602052604083205481811061202b57817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef926020928587528684520360408620558060025403600255604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608490fd5b60018060a01b031660005260006020526120f4604060002054601860205260406000205490611e9b565b90565b6001600160a01b0390811691821561221257169182156121c15760008281528060205260408120549180831061216d57604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815220818154019055604051908152a3565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b600b5460408051633037408d60e01b81526001600160a01b0384811660048301524260248301529283169695929493909260209283856044818c5afa94851561242857600095612433575b508360249697989982519788809263d2dcd93360e01b82524260048301525afa958615612428576000966123f9575b506028870287810460281488151715610e5857606490048661238a575b7f47211fe8b1eecabef1c013b28eb9caa892fd6bf2d3f1f5111d08cc115de841659860e0989796959493929180891015612384575087935b169687600052600d855261235d826000205485846000205561235886600e54611d09565b611e9b565b9485600e5582519889528801528601526060850152608084015260a083015260c0820152a1565b93612334565b9695949392919061239b8583611e6c565b98603c8a0299808b04603c1490151715610e58576123e960e09960646123e28a7f47211fe8b1eecabef1c013b28eb9caa892fd6bf2d3f1f5111d08cc115de841659e611e4c565b0490611d09565b91929394959697985098506122fc565b90958482813d8311612421575b6124108183611c8f565b810103126103f257505194386122df565b503d612406565b50513d6000823e3d90fd5b9894848a813d831161245d575b61244a8183611c8f565b810103126103f2575097519397836122b0565b503d612440565b60405160009160c0820167ffffffffffffffff811183821017612921576040528260208301528260408301528260608301528260808301528260a0830152600f548083528352601060205260408320546020830152815183526011602052604083205460408301526015548060608401526080830152600c548060a08401526020830151111561283b575b600a546001600160a01b0316803b156115ae57838091602460405180948193638aca6a2360e01b83523060048401525af1801561143a57612828575b5060ff60065460a01c1661281d575b60208201518042116125ef575b50815160018101809111610ace579081604092845280600f55845260106020524282852055818301518351855260116020528285205560018060a01b031691828452600d602052670de0b6b3a76400006125ba838620546125b48585015160126020528689205490611e9b565b90611e6c565b0483855260146020526125d1838620918254611d09565b90550151908252601260205260408220556013602052604042912055565b600e5462093a808201808311610e585762093a80900462093a8081029080820462093a801490151715610e585742811015612816575b600a546001600160a01b031692869291905b6101f4841061264a575b50505050612547565b6126548183611e9b565b9062093a808104918262093a8081020462093a801483151715610e5857604051630cb8c08d60e31b815230600482015262093a809390930260248401526020836044818a5afa92831561280b579189918694938c946127d0575b5084612701575b50505050504281146126fb578062093a8081018111610e58574262093a80820110156126f15762093a806126ea910193611e8c565b9290612637565b506126ea42611f13565b80612641565b60a0830151868382101591826127c6575b50501561279d57506127758361275f84604061275789612752612752986125b46127476127839d61278d9f9d60600151611e6c565b9160a0880151611e9b565b611e4c565b910151611d09565b60408d015260808c015160608d01819052611e6c565b6125b460a08c015187611e9b565b6040880151611d09565b60408701525b81388781806126b5565b61275291506127b76127839460606127bc97950151611e6c565b611e6c565b6040870152612793565b1090508638612712565b9250925092506020813d602011612803575b816127ef60209383611c8f565b81010312610b1557849289915192386126ae565b3d91506127e2565b6040513d8c823e3d90fd5b5042612625565b82606083015261253a565b61283490939193611c7b565b913861252b565b60095460405163277dbafb60e01b81526001600160a01b039160209082906004908290899087165af19081156129165785916128e3575b50600c55600954604051631627391760e11b81529160209183916004918391165afa90811561143a5784916128b1575b508060808401526015556124ef565b90506020813d6020116128db575b816128cc60209383611c8f565b810103126115ae5751386128a2565b3d91506128bf565b90506020813d60201161290e575b816128fe60209383611c8f565b8101031261153c57516004612872565b3d91506128f1565b6040513d87823e3d90fd5b634e487b7160e01b84526041600452602484fd5b1561293c57565b60405162461bcd60e51b8152602060048201526005602482015264475030303160d81b6044820152606490fd5b1561297057565b634e487b7160e01b600052600160045260246000fd5b604080516370a0823160e01b8082526001600160a01b038086166004840181905292959394169260209291908387602481885afa968715612ade57600097612aae575b5060006044859288519063a9059cbb60e01b825260048201528a602482015282885af13d15601f3d1160016000511416171615612a7857906024839286519586938492835260048301525afa928315612a6e5750600092612a3d575b50506120f491612a3491611e9b565b91821115612969565b81819392933d8311612a67575b612a548183611c8f565b810103126103f257505181612a34612a25565b503d612a4a565b513d6000823e3d90fd5b845162461bcd60e51b815260048101849052600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b90968482813d8311612ad7575b612ac58183611c8f565b810103126103f25750519560006129c9565b503d612abb565b86513d6000823e3d90fdfea26469706673582212206bed956b7effe049f0ab60543dc33361256b1d1e0807d7f2a3c83c8870d6113064736f6c63430008110033

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

000000000000000000000000c353bf07405304aeab75f4c2fac7e88d6a68f98e00000000000000000000000094afb2c17af24cfacf19f364628f459dfab2688f000000000000000000000000c53c83d26151dbcffa349fae20b6155299e87a35

-----Decoded View---------------
Arg [0] : _stakedToken (address): 0xc353Bf07405304AeaB75F4C2Fac7E88D6A68f98e
Arg [1] : _minter (address): 0x94aFb2C17af24cFAcf19f364628F459dfAB2688f
Arg [2] : _permit2Address (address): 0xC53c83d26151dBcfFa349Fae20B6155299E87a35

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000c353bf07405304aeab75f4c2fac7e88d6a68f98e
Arg [1] : 00000000000000000000000094afb2c17af24cfacf19f364628f459dfab2688f
Arg [2] : 000000000000000000000000c53c83d26151dbcffa349fae20b6155299e87a35


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

The HOPE Ecosystem all-in-one HOPE Ecosystem provides a comprehensive set of use cases for $HOPE, including swap, lending, custody, clearing, and settlement, while incentivizing users to participate in the ecosystem and community governance through $LT.

Validator Index Block Amount
View All Withdrawals

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

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