ETH Price: $2,032.64 (-1.05%)
Gas: 42 Gwei
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multi Chain

Multichain Addresses

Transaction Hash
Method
Block
From
To
Value
0x9e486fe580cf069ae425fb34d16e0389dd3bd4f75361a2370262de1f3278b2f9Staking(pending)2023-11-26 1:53:274 days 1 hr ago1700963607IN
HOPE: stHOPE Token
0 ETH(Pending)(Pending)
Redeem All186676742023-11-28 4:14:111 day 23 hrs ago1701144851IN
HOPE: stHOPE Token
0 ETH0.0122309831.15072291
Redeem All186596532023-11-27 1:16:233 days 2 hrs ago1701047783IN
HOPE: stHOPE Token
0 ETH0.0088978822.76070437
Redeem All186492662023-11-25 14:20:114 days 12 hrs ago1700922011IN
HOPE: stHOPE Token
0 ETH0.00810919.90765868
Unstaking186491912023-11-25 14:05:114 days 13 hrs ago1700921111IN
HOPE: stHOPE Token
0 ETH0.0094285821.64798702
Unstaking186422282023-11-24 14:42:115 days 12 hrs ago1700836931IN
HOPE: stHOPE Token
0 ETH0.0138770332.11890093
Redeem All186389572023-11-24 3:41:475 days 23 hrs ago1700797307IN
HOPE: stHOPE Token
0 ETH0.0081216120.3176918
Redeem All186362822023-11-23 18:40:596 days 8 hrs ago1700764859IN
HOPE: stHOPE Token
0 ETH0.0128286733.52649406
Redeem All186262722023-11-22 9:04:597 days 18 hrs ago1700643899IN
HOPE: stHOPE Token
0 ETH0.0098078224.6947681
Redeem All186257982023-11-22 7:29:237 days 19 hrs ago1700638163IN
HOPE: stHOPE Token
0 ETH0.0094274224.88345691
Approve186061542023-11-19 13:28:3510 days 13 hrs ago1700400515IN
HOPE: stHOPE Token
0 ETH0.0004569815.76477199
Redeem All186023302023-11-19 0:37:2311 days 2 hrs ago1700354243IN
HOPE: stHOPE Token
0 ETH0.0079836119.97017613
Redeem All185985592023-11-18 11:54:4711 days 15 hrs ago1700308487IN
HOPE: stHOPE Token
0 ETH0.0069144117.83619303
Redeem All185974282023-11-18 8:04:3511 days 19 hrs ago1700294675IN
HOPE: stHOPE Token
0 ETH0.0076024819.19999659
Redeem All185891462023-11-17 4:13:3512 days 23 hrs ago1700194415IN
HOPE: stHOPE Token
0 ETH0.0093840922.24856865
Approve185872302023-11-16 21:47:4713 days 5 hrs ago1700171267IN
HOPE: stHOPE Token
0 ETH0.0014788731.8969941
Redeem All185807322023-11-16 0:01:1114 days 3 hrs ago1700092871IN
HOPE: stHOPE Token
0 ETH0.0414502338.50269833
Unstaking185721112023-11-14 19:04:1115 days 8 hrs ago1699988651IN
HOPE: stHOPE Token
0 ETH0.0369998890.85366299
Unstaking185539432023-11-12 6:07:1117 days 21 hrs ago1699769231IN
HOPE: stHOPE Token
0 ETH0.0072816518.43704427
Redeem All185445062023-11-10 22:24:1119 days 4 hrs ago1699655051IN
HOPE: stHOPE Token
0 ETH0.0130437434.24139892
Unstaking185332712023-11-09 8:43:5920 days 18 hrs ago1699519439IN
HOPE: stHOPE Token
0 ETH0.012968327.9379648
Approve185259412023-11-08 8:07:2321 days 19 hrs ago1699430843IN
HOPE: stHOPE Token
0 ETH0.0012268826.46192124
Unstaking184956192023-11-04 2:11:3526 days 1 hr ago1699063895IN
HOPE: stHOPE Token
0 ETH0.0046542813.00443859
Unstaking184908022023-11-03 10:01:2326 days 17 hrs ago1699005683IN
HOPE: stHOPE Token
0 ETH0.0076223822.055507
Approve184624942023-10-30 10:51:3530 days 16 hrs ago1698663095IN
HOPE: stHOPE Token
0 ETH0.0007265915.6715603
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 14 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.

Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.