CRO Price: $0.08 (-1.59%)

Contract

0x4274b37fF2Ad353a97e414f56970CF5E0Fa6dA4F

Overview

CRO Balance

Cronos Chain LogoCronos Chain LogoCronos Chain Logo0 CRO

CRO Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Delegate152655572024-08-10 0:58:1756 days ago1723251497IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132386815,048.5
Delegate150798352024-07-28 18:12:1369 days ago1722190333IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Transfer150109792024-07-24 6:19:3673 days ago1721801976IN
0x4274b37f...E0Fa6dA4F
0 CRO0.627922335,048.5
Delegate145739242024-06-25 16:32:30102 days ago1719333150IN
0x4274b37f...E0Fa6dA4F
0 CRO0.13635965,200
Delegate144234992024-06-15 20:57:23112 days ago1718485043IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Approve143719912024-06-12 11:16:24115 days ago1718190984IN
0x4274b37f...E0Fa6dA4F
0 CRO0.23538055,050
Delegate143061382024-06-08 3:07:30119 days ago1717816050IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Delegate141870602024-05-31 6:47:59127 days ago1717138079IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Delegate141479182024-05-28 16:50:22130 days ago1716915022IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Delegate138162732024-05-06 20:08:03152 days ago1715026083IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Approve137848682024-05-04 18:17:04154 days ago1714846624IN
0x4274b37f...E0Fa6dA4F
0 CRO0.23538055,050
Delegate137650482024-05-03 10:46:48155 days ago1714733208IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Approve137611422024-05-03 4:35:24155 days ago1714710924IN
0x4274b37f...E0Fa6dA4F
0 CRO0.23538055,050
Delegate136839472024-04-28 2:02:28160 days ago1714269748IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Delegate136114652024-04-23 7:18:58165 days ago1713856738IN
0x4274b37f...E0Fa6dA4F
0 CRO0.670210735,025.5
Delegate136084892024-04-23 2:38:12165 days ago1713839892IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Delegate135952002024-04-22 5:38:09166 days ago1713764289IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Delegate135735542024-04-20 19:33:35168 days ago1713641615IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Transfer135424392024-04-18 18:31:48170 days ago1713465108IN
0x4274b37f...E0Fa6dA4F
0 CRO0.63061375,050
Delegate135419272024-04-18 17:43:30170 days ago1713462210IN
0x4274b37f...E0Fa6dA4F
0 CRO0.132426155,050
Transfer135166462024-04-17 1:54:31171 days ago1713318871IN
0x4274b37f...E0Fa6dA4F
0 CRO0.541577835,048.5
Transfer135166382024-04-17 1:53:46171 days ago1713318826IN
0x4274b37f...E0Fa6dA4F
0 CRO0.541577835,048.5
Transfer135166252024-04-17 1:52:32171 days ago1713318752IN
0x4274b37f...E0Fa6dA4F
0 CRO0.541577835,048.5
Transfer135166162024-04-17 1:51:41171 days ago1713318701IN
0x4274b37f...E0Fa6dA4F
0 CRO0.541577835,048.5
Transfer135165992024-04-17 1:50:05171 days ago1713318605IN
0x4274b37f...E0Fa6dA4F
0 CRO0.541577835,048.5
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Ticket

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 2000 runs

Other Settings:
default evmVersion, GNU GPLv3 license
File 1 of 21 : Ticket.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "./libraries/ExtendedSafeCastLib.sol";
import "./libraries/TwabLib.sol";
import "./interfaces/ITicket.sol";
import "./ControlledToken.sol";

/**
  * @title  PoolTogether V4 Ticket
  * @author PoolTogether Inc Team
  * @notice The Ticket extends the standard ERC20 and ControlledToken interfaces with time-weighted average balance functionality.
            The average balance held by a user between two timestamps can be calculated, as well as the historic balance.  The
            historic total supply is available as well as the average total supply between two timestamps.

            A user may "delegate" their balance; increasing another user's historic balance while retaining their tokens.
*/
contract Ticket is ControlledToken, ITicket {
    using SafeERC20 for IERC20;
    using ExtendedSafeCastLib for uint256;

    // solhint-disable-next-line var-name-mixedcase
    bytes32 private immutable _DELEGATE_TYPEHASH =
        keccak256("Delegate(address user,address delegate,uint256 nonce,uint256 deadline)");

    /// @notice Record of token holders TWABs for each account.
    mapping(address => TwabLib.Account) internal userTwabs;

    /// @notice Record of tickets total supply and ring buff parameters used for observation.
    TwabLib.Account internal totalSupplyTwab;

    /// @notice Mapping of delegates.  Each address can delegate their ticket power to another.
    mapping(address => address) internal delegates;

    /* ============ Constructor ============ */

    /**
     * @notice Constructs Ticket with passed parameters.
     * @param _name ERC20 ticket token name.
     * @param _symbol ERC20 ticket token symbol.
     * @param decimals_ ERC20 ticket token decimals.
     * @param _controller ERC20 ticket controller address (ie: Prize Pool address).
     */
    constructor(
        string memory _name,
        string memory _symbol,
        uint8 decimals_,
        address _controller
    ) ControlledToken(_name, _symbol, decimals_, _controller) {}

    /* ============ External Functions ============ */

    /// @inheritdoc ITicket
    function getAccountDetails(address _user)
        external
        view
        override
        returns (TwabLib.AccountDetails memory)
    {
        return userTwabs[_user].details;
    }

    /// @inheritdoc ITicket
    function getTwab(address _user, uint16 _index)
        external
        view
        override
        returns (ObservationLib.Observation memory)
    {
        return userTwabs[_user].twabs[_index];
    }

    /// @inheritdoc ITicket
    function getBalanceAt(address _user, uint64 _target) external view override returns (uint256) {
        TwabLib.Account storage account = userTwabs[_user];

        return
            TwabLib.getBalanceAt(
                account.twabs,
                account.details,
                uint32(_target),
                uint32(block.timestamp)
            );
    }

    /// @inheritdoc ITicket
    function getAverageBalancesBetween(
        address _user,
        uint64[] calldata _startTimes,
        uint64[] calldata _endTimes
    ) external view override returns (uint256[] memory) {
        return _getAverageBalancesBetween(userTwabs[_user], _startTimes, _endTimes);
    }

    /// @inheritdoc ITicket
    function getAverageTotalSuppliesBetween(
        uint64[] calldata _startTimes,
        uint64[] calldata _endTimes
    ) external view override returns (uint256[] memory) {
        return _getAverageBalancesBetween(totalSupplyTwab, _startTimes, _endTimes);
    }

    /// @inheritdoc ITicket
    function getAverageBalanceBetween(
        address _user,
        uint64 _startTime,
        uint64 _endTime
    ) external view override returns (uint256) {
        TwabLib.Account storage account = userTwabs[_user];

        return
            TwabLib.getAverageBalanceBetween(
                account.twabs,
                account.details,
                uint32(_startTime),
                uint32(_endTime),
                uint32(block.timestamp)
            );
    }

    /// @inheritdoc ITicket
    function getBalancesAt(address _user, uint64[] calldata _targets)
        external
        view
        override
        returns (uint256[] memory)
    {
        uint256 length = _targets.length;
        uint256[] memory _balances = new uint256[](length);

        TwabLib.Account storage twabContext = userTwabs[_user];
        TwabLib.AccountDetails memory details = twabContext.details;

        for (uint256 i = 0; i < length; i++) {
            _balances[i] = TwabLib.getBalanceAt(
                twabContext.twabs,
                details,
                uint32(_targets[i]),
                uint32(block.timestamp)
            );
        }

        return _balances;
    }

    /// @inheritdoc ITicket
    function getTotalSupplyAt(uint64 _target) external view override returns (uint256) {
        return
            TwabLib.getBalanceAt(
                totalSupplyTwab.twabs,
                totalSupplyTwab.details,
                uint32(_target),
                uint32(block.timestamp)
            );
    }

    /// @inheritdoc ITicket
    function getTotalSuppliesAt(uint64[] calldata _targets)
        external
        view
        override
        returns (uint256[] memory)
    {
        uint256 length = _targets.length;
        uint256[] memory totalSupplies = new uint256[](length);

        TwabLib.AccountDetails memory details = totalSupplyTwab.details;

        for (uint256 i = 0; i < length; i++) {
            totalSupplies[i] = TwabLib.getBalanceAt(
                totalSupplyTwab.twabs,
                details,
                uint32(_targets[i]),
                uint32(block.timestamp)
            );
        }

        return totalSupplies;
    }

    /// @inheritdoc ITicket
    function delegateOf(address _user) external view override returns (address) {
        return delegates[_user];
    }

    /// @inheritdoc ITicket
    function controllerDelegateFor(address _user, address _to) external override onlyController {
        _delegate(_user, _to);
    }

    /// @inheritdoc ITicket
    function delegateWithSignature(
        address _user,
        address _newDelegate,
        uint256 _deadline,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    ) external virtual override {
        require(block.timestamp <= _deadline, "Ticket/delegate-expired-deadline");

        bytes32 structHash = keccak256(abi.encode(_DELEGATE_TYPEHASH, _user, _newDelegate, _useNonce(_user), _deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, _v, _r, _s);
        require(signer == _user, "Ticket/delegate-invalid-signature");

        _delegate(_user, _newDelegate);
    }

    /// @inheritdoc ITicket
    function delegate(address _to) external virtual override {
        _delegate(msg.sender, _to);
    }

    /// @notice Delegates a users chance to another
    /// @param _user The user whose balance should be delegated
    /// @param _to The delegate
    function _delegate(address _user, address _to) internal {
        uint256 balance = balanceOf(_user);
        address currentDelegate = delegates[_user];

        if (currentDelegate == _to) {
            return;
        }

        delegates[_user] = _to;

        _transferTwab(currentDelegate, _to, balance);

        emit Delegated(_user, _to);
    }

    /* ============ Internal Functions ============ */

    /**
     * @notice Retrieves the average balances held by a user for a given time frame.
     * @param _account The user whose balance is checked.
     * @param _startTimes The start time of the time frame.
     * @param _endTimes The end time of the time frame.
     * @return The average balance that the user held during the time frame.
     */
    function _getAverageBalancesBetween(
        TwabLib.Account storage _account,
        uint64[] calldata _startTimes,
        uint64[] calldata _endTimes
    ) internal view returns (uint256[] memory) {
        uint256 startTimesLength = _startTimes.length;
        require(startTimesLength == _endTimes.length, "Ticket/start-end-times-length-match");

        TwabLib.AccountDetails memory accountDetails = _account.details;

        uint256[] memory averageBalances = new uint256[](startTimesLength);
        uint32 currentTimestamp = uint32(block.timestamp);

        for (uint256 i = 0; i < startTimesLength; i++) {
            averageBalances[i] = TwabLib.getAverageBalanceBetween(
                _account.twabs,
                accountDetails,
                uint32(_startTimes[i]),
                uint32(_endTimes[i]),
                currentTimestamp
            );
        }

        return averageBalances;
    }

    // @inheritdoc ERC20
    function _beforeTokenTransfer(address _from, address _to, uint256 _amount) internal override {
        if (_from == _to) {
            return;
        }

        address _fromDelegate;
        if (_from != address(0)) {
            _fromDelegate = delegates[_from];
        }

        address _toDelegate;
        if (_to != address(0)) {
            _toDelegate = delegates[_to];
        }

        _transferTwab(_fromDelegate, _toDelegate, _amount);
    }

    /// @notice Transfers the given TWAB balance from one user to another
    /// @param _from The user to transfer the balance from.  May be zero in the event of a mint.
    /// @param _to The user to transfer the balance to.  May be zero in the event of a burn.
    /// @param _amount The balance that is being transferred.
    function _transferTwab(address _from, address _to, uint256 _amount) internal {
        // If we are transferring tokens from a delegated account to an undelegated account
        if (_from != address(0)) {
            _decreaseUserTwab(_from, _amount);

            if (_to == address(0)) {
                _decreaseTotalSupplyTwab(_amount);
            }
        }

        // If we are transferring tokens from an undelegated account to a delegated account
        if (_to != address(0)) {
            _increaseUserTwab(_to, _amount);

            if (_from == address(0)) {
                _increaseTotalSupplyTwab(_amount);
            }
        }
    }

    /**
     * @notice Increase `_to` TWAB balance.
     * @param _to Address of the delegate.
     * @param _amount Amount of tokens to be added to `_to` TWAB balance.
     */
    function _increaseUserTwab(
        address _to,
        uint256 _amount
    ) internal {
        if (_amount == 0) {
            return;
        }

        TwabLib.Account storage _account = userTwabs[_to];

        (
            TwabLib.AccountDetails memory accountDetails,
            ObservationLib.Observation memory twab,
            bool isNew
        ) = TwabLib.increaseBalance(_account, _amount.toUint208(), uint32(block.timestamp));

        _account.details = accountDetails;

        if (isNew) {
            emit NewUserTwab(_to, twab);
        }
    }

    /**
     * @notice Decrease `_to` TWAB balance.
     * @param _to Address of the delegate.
     * @param _amount Amount of tokens to be added to `_to` TWAB balance.
     */
    function _decreaseUserTwab(
        address _to,
        uint256 _amount
    ) internal {
        if (_amount == 0) {
            return;
        }

        TwabLib.Account storage _account = userTwabs[_to];

        (
            TwabLib.AccountDetails memory accountDetails,
            ObservationLib.Observation memory twab,
            bool isNew
        ) = TwabLib.decreaseBalance(
                _account,
                _amount.toUint208(),
                "Ticket/twab-burn-lt-balance",
                uint32(block.timestamp)
            );

        _account.details = accountDetails;

        if (isNew) {
            emit NewUserTwab(_to, twab);
        }
    }

    /// @notice Decreases the total supply twab.  Should be called anytime a balance moves from delegated to undelegated
    /// @param _amount The amount to decrease the total by
    function _decreaseTotalSupplyTwab(uint256 _amount) internal {
        if (_amount == 0) {
            return;
        }

        (
            TwabLib.AccountDetails memory accountDetails,
            ObservationLib.Observation memory tsTwab,
            bool tsIsNew
        ) = TwabLib.decreaseBalance(
                totalSupplyTwab,
                _amount.toUint208(),
                "Ticket/burn-amount-exceeds-total-supply-twab",
                uint32(block.timestamp)
            );

        totalSupplyTwab.details = accountDetails;

        if (tsIsNew) {
            emit NewTotalSupplyTwab(tsTwab);
        }
    }

    /// @notice Increases the total supply twab.  Should be called anytime a balance moves from undelegated to delegated
    /// @param _amount The amount to increase the total by
    function _increaseTotalSupplyTwab(uint256 _amount) internal {
        if (_amount == 0) {
            return;
        }

        (
            TwabLib.AccountDetails memory accountDetails,
            ObservationLib.Observation memory _totalSupply,
            bool tsIsNew
        ) = TwabLib.increaseBalance(totalSupplyTwab, _amount.toUint208(), uint32(block.timestamp));

        totalSupplyTwab.details = accountDetails;

        if (tsIsNew) {
            emit NewTotalSupplyTwab(_totalSupply);
        }
    }
}

File 2 of 21 : ERC20.sol
// SPDX-License-Identifier: MIT

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.zeppelin.solutions/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:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, 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}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), 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}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - 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) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][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) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * 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:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, 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;
        _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;
        }
        _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 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 3 of 21 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

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

File 4 of 21 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

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 5 of 21 : draft-ERC20Permit.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./draft-IERC20Permit.sol";
import "../ERC20.sol";
import "../../../utils/cryptography/draft-EIP712.sol";
import "../../../utils/cryptography/ECDSA.sol";
import "../../../utils/Counters.sol";

/**
 * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * _Available since v3.4._
 */
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
    using Counters for Counters.Counter;

    mapping(address => Counters.Counter) private _nonces;

    // solhint-disable-next-line var-name-mixedcase
    bytes32 private immutable _PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
     */
    constructor(string memory name) EIP712(name, "1") {}

    /**
     * @dev See {IERC20Permit-permit}.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");

        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        _approve(owner, spender, value);
    }

    /**
     * @dev See {IERC20Permit-nonces}.
     */
    function nonces(address owner) public view virtual override returns (uint256) {
        return _nonces[owner].current();
    }

    /**
     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * @dev "Consume a nonce": return the current value and increment.
     *
     * _Available since v4.1._
     */
    function _useNonce(address owner) internal virtual returns (uint256 current) {
        Counters.Counter storage nonce = _nonces[owner];
        current = nonce.current();
        nonce.increment();
    }
}

File 6 of 21 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 7 of 21 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 8 of 21 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 9 of 21 : Context.sol
// SPDX-License-Identifier: MIT

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 10 of 21 : Counters.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

File 11 of 21 : ECDSA.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s;
        uint8 v;
        assembly {
            s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            v := add(shr(255, vs), 27)
        }
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 12 of 21 : draft-EIP712.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./ECDSA.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 */
abstract contract EIP712 {
    /* solhint-disable var-name-mixedcase */
    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
    uint256 private immutable _CACHED_CHAIN_ID;

    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;
    bytes32 private immutable _TYPE_HASH;

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        bytes32 typeHash = keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
        _CACHED_CHAIN_ID = block.chainid;
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
        _TYPE_HASH = typeHash;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (block.chainid == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }
}

File 13 of 21 : SafeCast.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 14 of 21 : ControlledToken.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";

import "./interfaces/IControlledToken.sol";

/**
 * @title  PoolTogether V4 Controlled ERC20 Token
 * @author PoolTogether Inc Team
 * @notice  ERC20 Tokens with a controller for minting & burning
 */
contract ControlledToken is ERC20Permit, IControlledToken {
    /* ============ Global Variables ============ */

    /// @notice Interface to the contract responsible for controlling mint/burn
    address public override immutable controller;

    /// @notice ERC20 controlled token decimals.
    uint8 private immutable _decimals;

    /* ============ Events ============ */

    /// @dev Emitted when contract is deployed
    event Deployed(string name, string symbol, uint8 decimals, address indexed controller);

    /* ============ Modifiers ============ */

    /// @dev Function modifier to ensure that the caller is the controller contract
    modifier onlyController() {
        require(msg.sender == address(controller), "ControlledToken/only-controller");
        _;
    }

    /* ============ Constructor ============ */

    /// @notice Deploy the Controlled Token with Token Details and the Controller
    /// @param _name The name of the Token
    /// @param _symbol The symbol for the Token
    /// @param decimals_ The number of decimals for the Token
    /// @param _controller Address of the Controller contract for minting & burning
    constructor(
        string memory _name,
        string memory _symbol,
        uint8 decimals_,
        address _controller
    ) ERC20Permit("PoolTogether ControlledToken") ERC20(_name, _symbol) {
        require(address(_controller) != address(0), "ControlledToken/controller-not-zero-address");
        controller = _controller;

        require(decimals_ > 0, "ControlledToken/decimals-gt-zero");
        _decimals = decimals_;

        emit Deployed(_name, _symbol, decimals_, _controller);
    }

    /* ============ External Functions ============ */

    /// @notice Allows the controller to mint tokens for a user account
    /// @dev May be overridden to provide more granular control over minting
    /// @param _user Address of the receiver of the minted tokens
    /// @param _amount Amount of tokens to mint
    function controllerMint(address _user, uint256 _amount)
        external
        virtual
        override
        onlyController
    {
        _mint(_user, _amount);
    }

    /// @notice Allows the controller to burn tokens from a user account
    /// @dev May be overridden to provide more granular control over burning
    /// @param _user Address of the holder account to burn tokens from
    /// @param _amount Amount of tokens to burn
    function controllerBurn(address _user, uint256 _amount)
        external
        virtual
        override
        onlyController
    {
        _burn(_user, _amount);
    }

    /// @notice Allows an operator via the controller to burn tokens on behalf of a user account
    /// @dev May be overridden to provide more granular control over operator-burning
    /// @param _operator Address of the operator performing the burn action via the controller contract
    /// @param _user Address of the holder account to burn tokens from
    /// @param _amount Amount of tokens to burn
    function controllerBurnFrom(
        address _operator,
        address _user,
        uint256 _amount
    ) external virtual override onlyController {
        if (_operator != _user) {
            _approve(_user, _operator, allowance(_user, _operator) - _amount);
        }

        _burn(_user, _amount);
    }

    /// @notice Returns the ERC20 controlled token decimals.
    /// @dev This value should be equal to the decimals of the token used to deposit into the pool.
    /// @return uint8 decimals.
    function decimals() public view virtual override returns (uint8) {
        return _decimals;
    }
}

File 15 of 21 : IControlledToken.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/** @title IControlledToken
  * @author PoolTogether Inc Team
  * @notice ERC20 Tokens with a controller for minting & burning.
*/
interface IControlledToken is IERC20 {

    /** 
        @notice Interface to the contract responsible for controlling mint/burn
    */
    function controller() external view returns (address);

    /** 
      * @notice Allows the controller to mint tokens for a user account
      * @dev May be overridden to provide more granular control over minting
      * @param user Address of the receiver of the minted tokens
      * @param amount Amount of tokens to mint
    */
    function controllerMint(address user, uint256 amount) external;

    /** 
      * @notice Allows the controller to burn tokens from a user account
      * @dev May be overridden to provide more granular control over burning
      * @param user Address of the holder account to burn tokens from
      * @param amount Amount of tokens to burn
    */
    function controllerBurn(address user, uint256 amount) external;

    /** 
      * @notice Allows an operator via the controller to burn tokens on behalf of a user account
      * @dev May be overridden to provide more granular control over operator-burning
      * @param operator Address of the operator performing the burn action via the controller contract
      * @param user Address of the holder account to burn tokens from
      * @param amount Amount of tokens to burn
    */
    function controllerBurnFrom(
        address operator,
        address user,
        uint256 amount
    ) external;
}

File 16 of 21 : ITicket.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "../libraries/TwabLib.sol";
import "./IControlledToken.sol";

interface ITicket is IControlledToken {
    /**
     * @notice A struct containing details for an Account.
     * @param balance The current balance for an Account.
     * @param nextTwabIndex The next available index to store a new twab.
     * @param cardinality The number of recorded twabs (plus one!).
     */
    struct AccountDetails {
        uint224 balance;
        uint16 nextTwabIndex;
        uint16 cardinality;
    }

    /**
     * @notice Combines account details with their twab history.
     * @param details The account details.
     * @param twabs The history of twabs for this account.
     */
    struct Account {
        AccountDetails details;
        ObservationLib.Observation[65535] twabs;
    }

    /**
     * @notice Emitted when TWAB balance has been delegated to another user.
     * @param delegator Address of the delegator.
     * @param delegate Address of the delegate.
     */
    event Delegated(address indexed delegator, address indexed delegate);

    /**
     * @notice Emitted when ticket is initialized.
     * @param name Ticket name (eg: PoolTogether Dai Ticket (Compound)).
     * @param symbol Ticket symbol (eg: PcDAI).
     * @param decimals Ticket decimals.
     * @param controller Token controller address.
     */
    event TicketInitialized(string name, string symbol, uint8 decimals, address indexed controller);

    /**
     * @notice Emitted when a new TWAB has been recorded.
     * @param delegate The recipient of the ticket power (may be the same as the user).
     * @param newTwab Updated TWAB of a ticket holder after a successful TWAB recording.
     */
    event NewUserTwab(
        address indexed delegate,
        ObservationLib.Observation newTwab
    );

    /**
     * @notice Emitted when a new total supply TWAB has been recorded.
     * @param newTotalSupplyTwab Updated TWAB of tickets total supply after a successful total supply TWAB recording.
     */
    event NewTotalSupplyTwab(ObservationLib.Observation newTotalSupplyTwab);

    /**
     * @notice Retrieves the address of the delegate to whom `user` has delegated their tickets.
     * @dev Address of the delegate will be the zero address if `user` has not delegated their tickets.
     * @param user Address of the delegator.
     * @return Address of the delegate.
     */
    function delegateOf(address user) external view returns (address);

    /**
    * @notice Delegate time-weighted average balances to an alternative address.
    * @dev    Transfers (including mints) trigger the storage of a TWAB in delegate(s) account, instead of the
              targetted sender and/or recipient address(s).
    * @dev    To reset the delegate, pass the zero address (0x000.000) as `to` parameter.
    * @dev Current delegate address should be different from the new delegate address `to`.
    * @param  to Recipient of delegated TWAB.
    */
    function delegate(address to) external;

    /**
     * @notice Allows the controller to delegate on a users behalf.
     * @param user The user for whom to delegate
     * @param delegate The new delegate
     */
    function controllerDelegateFor(address user, address delegate) external;

    /**
     * @notice Allows a user to delegate via signature
     * @param user The user who is delegating
     * @param delegate The new delegate
     * @param deadline The timestamp by which this must be submitted
     * @param v The v portion of the ECDSA sig
     * @param r The r portion of the ECDSA sig
     * @param s The s portion of the ECDSA sig
     */
    function delegateWithSignature(
        address user,
        address delegate,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Gets a users twab context.  This is a struct with their balance, next twab index, and cardinality.
     * @param user The user for whom to fetch the TWAB context.
     * @return The TWAB context, which includes { balance, nextTwabIndex, cardinality }
     */
    function getAccountDetails(address user) external view returns (TwabLib.AccountDetails memory);

    /**
     * @notice Gets the TWAB at a specific index for a user.
     * @param user The user for whom to fetch the TWAB.
     * @param index The index of the TWAB to fetch.
     * @return The TWAB, which includes the twab amount and the timestamp.
     */
    function getTwab(address user, uint16 index)
        external
        view
        returns (ObservationLib.Observation memory);

    /**
     * @notice Retrieves `user` TWAB balance.
     * @param user Address of the user whose TWAB is being fetched.
     * @param timestamp Timestamp at which we want to retrieve the TWAB balance.
     * @return The TWAB balance at the given timestamp.
     */
    function getBalanceAt(address user, uint64 timestamp) external view returns (uint256);

    /**
     * @notice Retrieves `user` TWAB balances.
     * @param user Address of the user whose TWABs are being fetched.
     * @param timestamps Timestamps range at which we want to retrieve the TWAB balances.
     * @return `user` TWAB balances.
     */
    function getBalancesAt(address user, uint64[] calldata timestamps)
        external
        view
        returns (uint256[] memory);

    /**
     * @notice Retrieves the average balance held by a user for a given time frame.
     * @param user The user whose balance is checked.
     * @param startTime The start time of the time frame.
     * @param endTime The end time of the time frame.
     * @return The average balance that the user held during the time frame.
     */
    function getAverageBalanceBetween(
        address user,
        uint64 startTime,
        uint64 endTime
    ) external view returns (uint256);

    /**
     * @notice Retrieves the average balances held by a user for a given time frame.
     * @param user The user whose balance is checked.
     * @param startTimes The start time of the time frame.
     * @param endTimes The end time of the time frame.
     * @return The average balance that the user held during the time frame.
     */
    function getAverageBalancesBetween(
        address user,
        uint64[] calldata startTimes,
        uint64[] calldata endTimes
    ) external view returns (uint256[] memory);

    /**
     * @notice Retrieves the total supply TWAB balance at the given timestamp.
     * @param timestamp Timestamp at which we want to retrieve the total supply TWAB balance.
     * @return The total supply TWAB balance at the given timestamp.
     */
    function getTotalSupplyAt(uint64 timestamp) external view returns (uint256);

    /**
     * @notice Retrieves the total supply TWAB balance between the given timestamps range.
     * @param timestamps Timestamps range at which we want to retrieve the total supply TWAB balance.
     * @return Total supply TWAB balances.
     */
    function getTotalSuppliesAt(uint64[] calldata timestamps)
        external
        view
        returns (uint256[] memory);

    /**
     * @notice Retrieves the average total supply balance for a set of given time frames.
     * @param startTimes Array of start times.
     * @param endTimes Array of end times.
     * @return The average total supplies held during the time frame.
     */
    function getAverageTotalSuppliesBetween(
        uint64[] calldata startTimes,
        uint64[] calldata endTimes
    ) external view returns (uint256[] memory);
}

File 17 of 21 : ExtendedSafeCastLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library ExtendedSafeCastLib {

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 _value) internal pure returns (uint104) {
        require(_value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(_value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 _value) internal pure returns (uint208) {
        require(_value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(_value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 _value) internal pure returns (uint224) {
        require(_value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(_value);
    }
}

File 18 of 21 : ObservationLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/utils/math/SafeCast.sol";

import "./OverflowSafeComparatorLib.sol";
import "./RingBufferLib.sol";

/**
* @title Observation Library
* @notice This library allows one to store an array of timestamped values and efficiently binary search them.
* @dev Largely pulled from Uniswap V3 Oracle.sol: https://github.com/Uniswap/v3-core/blob/c05a0e2c8c08c460fb4d05cfdda30b3ad8deeaac/contracts/libraries/Oracle.sol
* @author PoolTogether Inc.
*/
library ObservationLib {
    using OverflowSafeComparatorLib for uint32;
    using SafeCast for uint256;

    /// @notice The maximum number of observations
    uint24 public constant MAX_CARDINALITY = 16777215; // 2**24

    /**
    * @notice Observation, which includes an amount and timestamp.
    * @param amount `amount` at `timestamp`.
    * @param timestamp Recorded `timestamp`.
    */
    struct Observation {
        uint224 amount;
        uint32 timestamp;
    }

    /**
    * @notice Fetches Observations `beforeOrAt` and `atOrAfter` a `_target`, eg: where [`beforeOrAt`, `atOrAfter`] is satisfied.
    * The result may be the same Observation, or adjacent Observations.
    * @dev The answer must be contained in the array used when the target is located within the stored Observation.
    * boundaries: older than the most recent Observation and younger, or the same age as, the oldest Observation.
    * @dev  If `_newestObservationIndex` is less than `_oldestObservationIndex`, it means that we've wrapped around the circular buffer.
    *       So the most recent observation will be at `_oldestObservationIndex + _cardinality - 1`, at the beginning of the circular buffer.
    * @param _observations List of Observations to search through.
    * @param _newestObservationIndex Index of the newest Observation. Right side of the circular buffer.
    * @param _oldestObservationIndex Index of the oldest Observation. Left side of the circular buffer.
    * @param _target Timestamp at which we are searching the Observation.
    * @param _cardinality Cardinality of the circular buffer we are searching through.
    * @param _time Timestamp at which we perform the binary search.
    * @return beforeOrAt Observation recorded before, or at, the target.
    * @return atOrAfter Observation recorded at, or after, the target.
    */
    function binarySearch(
        Observation[MAX_CARDINALITY] storage _observations,
        uint24 _newestObservationIndex,
        uint24 _oldestObservationIndex,
        uint32 _target,
        uint24 _cardinality,
        uint32 _time
    ) internal view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
        uint256 leftSide = _oldestObservationIndex;
        uint256 rightSide = _newestObservationIndex < leftSide
            ? leftSide + _cardinality - 1
            : _newestObservationIndex;
        uint256 currentIndex;

        while (true) {
            // We start our search in the middle of the `leftSide` and `rightSide`.
            // After each iteration, we narrow down the search to the left or the right side while still starting our search in the middle.
            currentIndex = (leftSide + rightSide) / 2;

            beforeOrAt = _observations[uint24(RingBufferLib.wrap(currentIndex, _cardinality))];
            uint32 beforeOrAtTimestamp = beforeOrAt.timestamp;

            // We've landed on an uninitialized timestamp, keep searching higher (more recently).
            if (beforeOrAtTimestamp == 0) {
                leftSide = currentIndex + 1;
                continue;
            }

            atOrAfter = _observations[uint24(RingBufferLib.nextIndex(currentIndex, _cardinality))];

            bool targetAtOrAfter = beforeOrAtTimestamp.lte(_target, _time);

            // Check if we've found the corresponding Observation.
            if (targetAtOrAfter && _target.lte(atOrAfter.timestamp, _time)) {
                break;
            }

            // If `beforeOrAtTimestamp` is greater than `_target`, then we keep searching lower. To the left of the current index.
            if (!targetAtOrAfter) {
                rightSide = currentIndex - 1;
            } else {
                // Otherwise, we keep searching higher. To the left of the current index.
                leftSide = currentIndex + 1;
            }
        }
    }
}

File 19 of 21 : OverflowSafeComparatorLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

/// @title OverflowSafeComparatorLib library to share comparator functions between contracts
/// @dev Code taken from Uniswap V3 Oracle.sol: https://github.com/Uniswap/v3-core/blob/3e88af408132fc957e3e406f65a0ce2b1ca06c3d/contracts/libraries/Oracle.sol
/// @author PoolTogether Inc.
library OverflowSafeComparatorLib {
    /// @notice 32-bit timestamps comparator.
    /// @dev safe for 0 or 1 overflows, `_a` and `_b` must be chronologically before or equal to time.
    /// @param _a A comparison timestamp from which to determine the relative position of `_timestamp`.
    /// @param _b Timestamp to compare against `_a`.
    /// @param _timestamp A timestamp truncated to 32 bits.
    /// @return bool Whether `_a` is chronologically < `_b`.
    function lt(
        uint32 _a,
        uint32 _b,
        uint32 _timestamp
    ) internal pure returns (bool) {
        // No need to adjust if there hasn't been an overflow
        if (_a <= _timestamp && _b <= _timestamp) return _a < _b;

        uint256 aAdjusted = _a > _timestamp ? _a : _a + 2**32;
        uint256 bAdjusted = _b > _timestamp ? _b : _b + 2**32;

        return aAdjusted < bAdjusted;
    }

    /// @notice 32-bit timestamps comparator.
    /// @dev safe for 0 or 1 overflows, `_a` and `_b` must be chronologically before or equal to time.
    /// @param _a A comparison timestamp from which to determine the relative position of `_timestamp`.
    /// @param _b Timestamp to compare against `_a`.
    /// @param _timestamp A timestamp truncated to 32 bits.
    /// @return bool Whether `_a` is chronologically <= `_b`.
    function lte(
        uint32 _a,
        uint32 _b,
        uint32 _timestamp
    ) internal pure returns (bool) {

        // No need to adjust if there hasn't been an overflow
        if (_a <= _timestamp && _b <= _timestamp) return _a <= _b;

        uint256 aAdjusted = _a > _timestamp ? _a : _a + 2**32;
        uint256 bAdjusted = _b > _timestamp ? _b : _b + 2**32;

        return aAdjusted <= bAdjusted;
    }

    /// @notice 32-bit timestamp subtractor
    /// @dev safe for 0 or 1 overflows, where `_a` and `_b` must be chronologically before or equal to time
    /// @param _a The subtraction left operand
    /// @param _b The subtraction right operand
    /// @param _timestamp The current time.  Expected to be chronologically after both.
    /// @return The difference between a and b, adjusted for overflow
    function checkedSub(
        uint32 _a,
        uint32 _b,
        uint32 _timestamp
    ) internal pure returns (uint32) {
        // No need to adjust if there hasn't been an overflow

        if (_a <= _timestamp && _b <= _timestamp) return _a - _b;

        uint256 aAdjusted = _a > _timestamp ? _a : _a + 2**32;
        uint256 bAdjusted = _b > _timestamp ? _b : _b + 2**32;

        return uint32(aAdjusted - bAdjusted);
    }
}

File 20 of 21 : RingBufferLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

library RingBufferLib {
    /**
    * @notice Returns wrapped TWAB index.
    * @dev  In order to navigate the TWAB circular buffer, we need to use the modulo operator.
    * @dev  For example, if `_index` is equal to 32 and the TWAB circular buffer is of `_cardinality` 32,
    *       it will return 0 and will point to the first element of the array.
    * @param _index Index used to navigate through the TWAB circular buffer.
    * @param _cardinality TWAB buffer cardinality.
    * @return TWAB index.
    */
    function wrap(uint256 _index, uint256 _cardinality) internal pure returns (uint256) {
        return _index % _cardinality;
    }

    /**
    * @notice Computes the negative offset from the given index, wrapped by the cardinality.
    * @dev  We add `_cardinality` to `_index` to be able to offset even if `_amount` is superior to `_cardinality`.
    * @param _index The index from which to offset
    * @param _amount The number of indices to offset.  This is subtracted from the given index.
    * @param _cardinality The number of elements in the ring buffer
    * @return Offsetted index.
     */
    function offset(
        uint256 _index,
        uint256 _amount,
        uint256 _cardinality
    ) internal pure returns (uint256) {
        return wrap(_index + _cardinality - _amount, _cardinality);
    }

    /// @notice Returns the index of the last recorded TWAB
    /// @param _nextIndex The next available twab index.  This will be recorded to next.
    /// @param _cardinality The cardinality of the TWAB history.
    /// @return The index of the last recorded TWAB
    function newestIndex(uint256 _nextIndex, uint256 _cardinality)
        internal
        pure
        returns (uint256)
    {
        if (_cardinality == 0) {
            return 0;
        }

        return wrap(_nextIndex + _cardinality - 1, _cardinality);
    }

    /// @notice Computes the ring buffer index that follows the given one, wrapped by cardinality
    /// @param _index The index to increment
    /// @param _cardinality The number of elements in the Ring Buffer
    /// @return The next index relative to the given index.  Will wrap around to 0 if the next index == cardinality
    function nextIndex(uint256 _index, uint256 _cardinality)
        internal
        pure
        returns (uint256)
    {
        return wrap(_index + 1, _cardinality);
    }
}

File 21 of 21 : TwabLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "./ExtendedSafeCastLib.sol";
import "./OverflowSafeComparatorLib.sol";
import "./RingBufferLib.sol";
import "./ObservationLib.sol";

/**
  * @title  PoolTogether V4 TwabLib (Library)
  * @author PoolTogether Inc Team
  * @dev    Time-Weighted Average Balance Library for ERC20 tokens.
  * @notice This TwabLib adds on-chain historical lookups to a user(s) time-weighted average balance.
            Each user is mapped to an Account struct containing the TWAB history (ring buffer) and
            ring buffer parameters. Every token.transfer() creates a new TWAB checkpoint. The new TWAB
            checkpoint is stored in the circular ring buffer, as either a new checkpoint or rewriting
            a previous checkpoint with new parameters. The TwabLib (using existing blocktimes of 1block/15sec)
            guarantees minimum 7.4 years of search history.
 */
library TwabLib {
    using OverflowSafeComparatorLib for uint32;
    using ExtendedSafeCastLib for uint256;

    /**
      * @notice Sets max ring buffer length in the Account.twabs Observation list.
                As users transfer/mint/burn tickets new Observation checkpoints are
                recorded. The current max cardinality guarantees a seven year minimum,
                of accurate historical lookups with current estimates of 1 new block
                every 15 seconds - assuming each block contains a transfer to trigger an
                observation write to storage.
      * @dev    The user Account.AccountDetails.cardinality parameter can NOT exceed
                the max cardinality variable. Preventing "corrupted" ring buffer lookup
                pointers and new observation checkpoints.

                The MAX_CARDINALITY in fact guarantees at least 7.4 years of records:
                If 14 = block time in seconds
                (2**24) * 14 = 234881024 seconds of history
                234881024 / (365 * 24 * 60 * 60) ~= 7.44 years
    */
    uint24 public constant MAX_CARDINALITY = 16777215; // 2**24

    /** @notice Struct ring buffer parameters for single user Account
      * @param balance       Current balance for an Account
      * @param nextTwabIndex Next uninitialized or updatable ring buffer checkpoint storage slot
      * @param cardinality   Current total "initialized" ring buffer checkpoints for single user AccountDetails.
                             Used to set initial boundary conditions for an efficient binary search.
    */
    struct AccountDetails {
        uint208 balance;
        uint24 nextTwabIndex;
        uint24 cardinality;
    }

    /// @notice Combines account details with their twab history
    /// @param details The account details
    /// @param twabs The history of twabs for this account
    struct Account {
        AccountDetails details;
        ObservationLib.Observation[MAX_CARDINALITY] twabs;
    }

    /// @notice Increases an account's balance and records a new twab.
    /// @param _account The account whose balance will be increased
    /// @param _amount The amount to increase the balance by
    /// @param _currentTime The current time
    /// @return accountDetails The new AccountDetails
    /// @return twab The user's latest TWAB
    /// @return isNew Whether the TWAB is new
    function increaseBalance(
        Account storage _account,
        uint208 _amount,
        uint32 _currentTime
    )
        internal
        returns (
            AccountDetails memory accountDetails,
            ObservationLib.Observation memory twab,
            bool isNew
        )
    {
        AccountDetails memory _accountDetails = _account.details;
        (accountDetails, twab, isNew) = _nextTwab(_account.twabs, _accountDetails, _currentTime);
        accountDetails.balance = _accountDetails.balance + _amount;
    }

    /** @notice Calculates the next TWAB checkpoint for an account with a decreasing balance.
     * @dev    With Account struct and amount decreasing calculates the next TWAB observable checkpoint.
     * @param _account        Account whose balance will be decreased
     * @param _amount         Amount to decrease the balance by
     * @param _revertMessage  Revert message for insufficient balance
     * @return accountDetails Updated Account.details struct
     * @return twab           TWAB observation (with decreasing average)
     * @return isNew          Whether TWAB is new or calling twice in the same block
     */
    function decreaseBalance(
        Account storage _account,
        uint208 _amount,
        string memory _revertMessage,
        uint32 _currentTime
    )
        internal
        returns (
            AccountDetails memory accountDetails,
            ObservationLib.Observation memory twab,
            bool isNew
        )
    {
        AccountDetails memory _accountDetails = _account.details;

        require(_accountDetails.balance >= _amount, _revertMessage);

        (accountDetails, twab, isNew) = _nextTwab(_account.twabs, _accountDetails, _currentTime);
        unchecked {
            accountDetails.balance -= _amount;
        }
    }

    /** @notice Calculates the average balance held by a user for a given time frame.
      * @dev    Finds the average balance between start and end timestamp epochs.
                Validates the supplied end time is within the range of elapsed time i.e. less then timestamp of now.
      * @param _twabs          Individual user Observation recorded checkpoints passed as storage pointer
      * @param _accountDetails User AccountDetails struct loaded in memory
      * @param _startTime      Start of timestamp range as an epoch
      * @param _endTime        End of timestamp range as an epoch
      * @param _currentTime    Block.timestamp
      * @return Average balance of user held between epoch timestamps start and end
    */
    function getAverageBalanceBetween(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _startTime,
        uint32 _endTime,
        uint32 _currentTime
    ) internal view returns (uint256) {
        uint32 endTime = _endTime > _currentTime ? _currentTime : _endTime;

        return
            _getAverageBalanceBetween(_twabs, _accountDetails, _startTime, endTime, _currentTime);
    }

    /// @notice Retrieves the oldest TWAB
    /// @param _twabs The storage array of twabs
    /// @param _accountDetails The TWAB account details
    /// @return index The index of the oldest TWAB in the twabs array
    /// @return twab The oldest TWAB
    function oldestTwab(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails
    ) internal view returns (uint24 index, ObservationLib.Observation memory twab) {
        index = _accountDetails.nextTwabIndex;
        twab = _twabs[index];

        // If the TWAB is not initialized we go to the beginning of the TWAB circular buffer at index 0
        if (twab.timestamp == 0) {
            index = 0;
            twab = _twabs[0];
        }
    }

    /// @notice Retrieves the newest TWAB
    /// @param _twabs The storage array of twabs
    /// @param _accountDetails The TWAB account details
    /// @return index The index of the newest TWAB in the twabs array
    /// @return twab The newest TWAB
    function newestTwab(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails
    ) internal view returns (uint24 index, ObservationLib.Observation memory twab) {
        index = uint24(RingBufferLib.newestIndex(_accountDetails.nextTwabIndex, MAX_CARDINALITY));
        twab = _twabs[index];
    }

    /// @notice Retrieves amount at `_targetTime` timestamp
    /// @param _twabs List of TWABs to search through.
    /// @param _accountDetails Accounts details
    /// @param _targetTime Timestamp at which the reserved TWAB should be for.
    /// @return uint256 TWAB amount at `_targetTime`.
    function getBalanceAt(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _targetTime,
        uint32 _currentTime
    ) internal view returns (uint256) {
        uint32 timeToTarget = _targetTime > _currentTime ? _currentTime : _targetTime;
        return _getBalanceAt(_twabs, _accountDetails, timeToTarget, _currentTime);
    }

    /// @notice Calculates the average balance held by a user for a given time frame.
    /// @param _startTime The start time of the time frame.
    /// @param _endTime The end time of the time frame.
    /// @return The average balance that the user held during the time frame.
    function _getAverageBalanceBetween(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _startTime,
        uint32 _endTime,
        uint32 _currentTime
    ) private view returns (uint256) {
        (uint24 oldestTwabIndex, ObservationLib.Observation memory oldTwab) = oldestTwab(
            _twabs,
            _accountDetails
        );

        (uint24 newestTwabIndex, ObservationLib.Observation memory newTwab) = newestTwab(
            _twabs,
            _accountDetails
        );

        ObservationLib.Observation memory startTwab = _calculateTwab(
            _twabs,
            _accountDetails,
            newTwab,
            oldTwab,
            newestTwabIndex,
            oldestTwabIndex,
            _startTime,
            _currentTime
        );

        ObservationLib.Observation memory endTwab = _calculateTwab(
            _twabs,
            _accountDetails,
            newTwab,
            oldTwab,
            newestTwabIndex,
            oldestTwabIndex,
            _endTime,
            _currentTime
        );

        // Difference in amount / time
        return (endTwab.amount - startTwab.amount) / OverflowSafeComparatorLib.checkedSub(endTwab.timestamp, startTwab.timestamp, _currentTime);
    }

    /** @notice Searches TWAB history and calculate the difference between amount(s)/timestamp(s) to return average balance
                between the Observations closes to the supplied targetTime.
      * @param _twabs          Individual user Observation recorded checkpoints passed as storage pointer
      * @param _accountDetails User AccountDetails struct loaded in memory
      * @param _targetTime     Target timestamp to filter Observations in the ring buffer binary search
      * @param _currentTime    Block.timestamp
      * @return uint256 Time-weighted average amount between two closest observations.
    */
    function _getBalanceAt(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _targetTime,
        uint32 _currentTime
    ) private view returns (uint256) {
        uint24 newestTwabIndex;
        ObservationLib.Observation memory afterOrAt;
        ObservationLib.Observation memory beforeOrAt;
        (newestTwabIndex, beforeOrAt) = newestTwab(_twabs, _accountDetails);

        // If `_targetTime` is chronologically after the newest TWAB, we can simply return the current balance
        if (beforeOrAt.timestamp.lte(_targetTime, _currentTime)) {
            return _accountDetails.balance;
        }

        uint24 oldestTwabIndex;
        // Now, set before to the oldest TWAB
        (oldestTwabIndex, beforeOrAt) = oldestTwab(_twabs, _accountDetails);

        // If `_targetTime` is chronologically before the oldest TWAB, we can early return
        if (_targetTime.lt(beforeOrAt.timestamp, _currentTime)) {
            return 0;
        }

        // Otherwise, we perform the `binarySearch`
        (beforeOrAt, afterOrAt) = ObservationLib.binarySearch(
            _twabs,
            newestTwabIndex,
            oldestTwabIndex,
            _targetTime,
            _accountDetails.cardinality,
            _currentTime
        );

        // Sum the difference in amounts and divide by the difference in timestamps.
        // The time-weighted average balance uses time measured between two epoch timestamps as
        // a constaint on the measurement when calculating the time weighted average balance.
        return
            (afterOrAt.amount - beforeOrAt.amount) / OverflowSafeComparatorLib.checkedSub(afterOrAt.timestamp, beforeOrAt.timestamp, _currentTime);
    }

    /** @notice Calculates a user TWAB for a target timestamp using the historical TWAB records.
                The balance is linearly interpolated: amount differences / timestamp differences
                using the simple (after.amount - before.amount / end.timestamp - start.timestamp) formula.
    /** @dev    Binary search in _calculateTwab fails when searching out of bounds. Thus, before
                searching we exclude target timestamps out of range of newest/oldest TWAB(s).
                IF a search is before or after the range we "extrapolate" a Observation from the expected state.
      * @param _twabs           Individual user Observation recorded checkpoints passed as storage pointer
      * @param _accountDetails  User AccountDetails struct loaded in memory
      * @param _newestTwab      Newest TWAB in history (end of ring buffer)
      * @param _oldestTwab      Olderst TWAB in history (end of ring buffer)
      * @param _newestTwabIndex Pointer in ring buffer to newest TWAB
      * @param _oldestTwabIndex Pointer in ring buffer to oldest TWAB
      * @param _targetTimestamp Epoch timestamp to calculate for time (T) in the TWAB
      * @param _time            Block.timestamp
      * @return accountDetails Updated Account.details struct
    */
    function _calculateTwab(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        ObservationLib.Observation memory _newestTwab,
        ObservationLib.Observation memory _oldestTwab,
        uint24 _newestTwabIndex,
        uint24 _oldestTwabIndex,
        uint32 _targetTimestamp,
        uint32 _time
    ) private view returns (ObservationLib.Observation memory) {
        // If `_targetTimestamp` is chronologically after the newest TWAB, we extrapolate a new one
        if (_newestTwab.timestamp.lt(_targetTimestamp, _time)) {
            return _computeNextTwab(_newestTwab, _accountDetails.balance, _targetTimestamp);
        }

        if (_newestTwab.timestamp == _targetTimestamp) {
            return _newestTwab;
        }

        if (_oldestTwab.timestamp == _targetTimestamp) {
            return _oldestTwab;
        }

        // If `_targetTimestamp` is chronologically before the oldest TWAB, we create a zero twab
        if (_targetTimestamp.lt(_oldestTwab.timestamp, _time)) {
            return ObservationLib.Observation({ amount: 0, timestamp: _targetTimestamp });
        }

        // Otherwise, both timestamps must be surrounded by twabs.
        (
            ObservationLib.Observation memory beforeOrAtStart,
            ObservationLib.Observation memory afterOrAtStart
        ) = ObservationLib.binarySearch(
                _twabs,
                _newestTwabIndex,
                _oldestTwabIndex,
                _targetTimestamp,
                _accountDetails.cardinality,
                _time
            );

        uint224 heldBalance = (afterOrAtStart.amount - beforeOrAtStart.amount) /
            OverflowSafeComparatorLib.checkedSub(afterOrAtStart.timestamp, beforeOrAtStart.timestamp, _time);

        return _computeNextTwab(beforeOrAtStart, heldBalance, _targetTimestamp);
    }

    /**
     * @notice Calculates the next TWAB using the newestTwab and updated balance.
     * @dev    Storage of the TWAB obersation is managed by the calling function and not _computeNextTwab.
     * @param _currentTwab    Newest Observation in the Account.twabs list
     * @param _currentBalance User balance at time of most recent (newest) checkpoint write
     * @param _time           Current block.timestamp
     * @return TWAB Observation
     */
    function _computeNextTwab(
        ObservationLib.Observation memory _currentTwab,
        uint224 _currentBalance,
        uint32 _time
    ) private pure returns (ObservationLib.Observation memory) {
        // New twab amount = last twab amount (or zero) + (current amount * elapsed seconds)
        return
            ObservationLib.Observation({
                amount: _currentTwab.amount +
                    _currentBalance *
                    (_time.checkedSub(_currentTwab.timestamp, _time)),
                timestamp: _time
            });
    }

    /// @notice Sets a new TWAB Observation at the next available index and returns the new account details.
    /// @dev Note that if _currentTime is before the last observation timestamp, it appears as an overflow
    /// @param _twabs The twabs array to insert into
    /// @param _accountDetails The current account details
    /// @param _currentTime The current time
    /// @return accountDetails The new account details
    /// @return twab The newest twab (may or may not be brand-new)
    /// @return isNew Whether the newest twab was created by this call
    function _nextTwab(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _currentTime
    )
        private
        returns (
            AccountDetails memory accountDetails,
            ObservationLib.Observation memory twab,
            bool isNew
        )
    {
        (, ObservationLib.Observation memory _newestTwab) = newestTwab(_twabs, _accountDetails);

        // if we're in the same block, return
        if (_newestTwab.timestamp == _currentTime) {
            return (_accountDetails, _newestTwab, false);
        }

        ObservationLib.Observation memory newTwab = _computeNextTwab(
            _newestTwab,
            _accountDetails.balance,
            _currentTime
        );

        _twabs[_accountDetails.nextTwabIndex] = newTwab;

        AccountDetails memory nextAccountDetails = push(_accountDetails);

        return (nextAccountDetails, newTwab, true);
    }

    /// @notice "Pushes" a new element on the AccountDetails ring buffer, and returns the new AccountDetails
    /// @param _accountDetails The account details from which to pull the cardinality and next index
    /// @return The new AccountDetails
    function push(AccountDetails memory _accountDetails)
        internal
        pure
        returns (AccountDetails memory)
    {
        _accountDetails.nextTwabIndex = uint24(
            RingBufferLib.nextIndex(_accountDetails.nextTwabIndex, MAX_CARDINALITY)
        );

        // Prevent the Account specific cardinality from exceeding the MAX_CARDINALITY.
        // The ring buffer length is limited by MAX_CARDINALITY. IF the account.cardinality
        // exceeds the max cardinality, new observations would be incorrectly set or the
        // observation would be out of "bounds" of the ring buffer. Once reached the
        // AccountDetails.cardinality will continue to be equal to max cardinality.
        if (_accountDetails.cardinality < MAX_CARDINALITY) {
            _accountDetails.cardinality += 1;
        }

        return _accountDetails;
    }
}

Settings
{
  "evmVersion": "berlin",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 2000
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address","name":"_controller","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":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"delegate","type":"address"}],"name":"Delegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"},{"indexed":true,"internalType":"address","name":"controller","type":"address"}],"name":"Deployed","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint224","name":"amount","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"indexed":false,"internalType":"struct ObservationLib.Observation","name":"newTotalSupplyTwab","type":"tuple"}],"name":"NewTotalSupplyTwab","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"components":[{"internalType":"uint224","name":"amount","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"indexed":false,"internalType":"struct ObservationLib.Observation","name":"newTwab","type":"tuple"}],"name":"NewUserTwab","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"},{"indexed":true,"internalType":"address","name":"controller","type":"address"}],"name":"TicketInitialized","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"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","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":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"controllerBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"controllerBurnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"controllerDelegateFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"controllerMint","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"_to","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"delegateOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_newDelegate","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"delegateWithSignature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getAccountDetails","outputs":[{"components":[{"internalType":"uint208","name":"balance","type":"uint208"},{"internalType":"uint24","name":"nextTwabIndex","type":"uint24"},{"internalType":"uint24","name":"cardinality","type":"uint24"}],"internalType":"struct TwabLib.AccountDetails","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint64","name":"_startTime","type":"uint64"},{"internalType":"uint64","name":"_endTime","type":"uint64"}],"name":"getAverageBalanceBetween","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint64[]","name":"_startTimes","type":"uint64[]"},{"internalType":"uint64[]","name":"_endTimes","type":"uint64[]"}],"name":"getAverageBalancesBetween","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64[]","name":"_startTimes","type":"uint64[]"},{"internalType":"uint64[]","name":"_endTimes","type":"uint64[]"}],"name":"getAverageTotalSuppliesBetween","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint64","name":"_target","type":"uint64"}],"name":"getBalanceAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint64[]","name":"_targets","type":"uint64[]"}],"name":"getBalancesAt","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64[]","name":"_targets","type":"uint64[]"}],"name":"getTotalSuppliesAt","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"_target","type":"uint64"}],"name":"getTotalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint16","name":"_index","type":"uint16"}],"name":"getTwab","outputs":[{"components":[{"internalType":"uint224","name":"amount","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"internalType":"struct ObservationLib.Observation","name":"","type":"tuple"}],"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":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

6101a06040527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9610120527f94019368dc6b2ee4ac32010c9d0081ec29874325b541829d001d22c296b5246c610180523480156200005c57600080fd5b5060405162003cbf38038062003cbf8339810160408190526200007f916200041f565b838383836040518060400160405280601c81526020017f506f6f6c546f67657468657220436f6e74726f6c6c6564546f6b656e0000000081525080604051806040016040528060018152602001603160f81b81525086868160039080519060200190620000ee929190620002e8565b50805162000104906004906020840190620002e8565b5050825160209384012082519284019290922060c083815260e08290524660a0818152604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818a018190528183019890985260608101959095526080808601939093523085830152805180860390920182529390920190925280519401939093209092526101005250506001600160a01b038116620002015760405162461bcd60e51b815260206004820152602b60248201527f436f6e74726f6c6c6564546f6b656e2f636f6e74726f6c6c65722d6e6f742d7a60448201526a65726f2d6164647265737360a81b60648201526084015b60405180910390fd5b6001600160601b0319606082901b166101405260ff8216620002665760405162461bcd60e51b815260206004820181905260248201527f436f6e74726f6c6c6564546f6b656e2f646563696d616c732d67742d7a65726f6044820152606401620001f8565b7fff0000000000000000000000000000000000000000000000000000000000000060f883901b16610160526040516001600160a01b038216907fde72fc29218361f33503847e6f32be813f9ec92fc7c772bb59e46675c890fd0e90620002d290879087908790620004f1565b60405180910390a25050505050505050620005b4565b828054620002f69062000561565b90600052602060002090601f0160209004810192826200031a576000855562000365565b82601f106200033557805160ff191683800117855562000365565b8280016001018555821562000365579182015b828111156200036557825182559160200191906001019062000348565b506200037392915062000377565b5090565b5b8082111562000373576000815560010162000378565b600082601f830112620003a057600080fd5b81516001600160401b0380821115620003bd57620003bd6200059e565b604051601f8301601f19908116603f01168101908282118183101715620003e857620003e86200059e565b816040528381528660208588010111156200040257600080fd5b620004158460208301602089016200052e565b9695505050505050565b600080600080608085870312156200043657600080fd5b84516001600160401b03808211156200044e57600080fd5b6200045c888389016200038e565b955060208701519150808211156200047357600080fd5b5062000482878288016200038e565b935050604085015160ff811681146200049a57600080fd5b60608601519092506001600160a01b0381168114620004b857600080fd5b939692955090935050565b60008151808452620004dd8160208601602086016200052e565b601f01601f19169290920160200192915050565b606081526000620005066060830186620004c3565b82810360208401526200051a8186620004c3565b91505060ff83166040830152949350505050565b60005b838110156200054b57818101518382015260200162000531565b838111156200055b576000848401525b50505050565b600181811c908216806200057657607f821691505b602082108114156200059857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052604160045260246000fd5b60805160a05160c05160e05161010051610120516101405160601c6101605160f81c61018051613678620006476000396000610d600152600061031b0152600081816105920152818161077a015281816108d001528181610a6e0152610c950152600061108501526000611669015260006116b801526000611693015260006116170152600061164001526136786000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c806368c7fd571161010f57806395d89b41116100a2578063a9059cbb11610071578063a9059cbb1461052e578063d505accf14610541578063dd62ed3e14610554578063f77c47911461058d57600080fd5b806395d89b41146104ed57806398b16f36146104f55780639ecb037014610508578063a457c2d71461051b57600080fd5b80638d22ea2a116100de5780638d22ea2a1461046d5780638e6d536a146104b457806390596dd1146104c7578063919974dc146104da57600080fd5b806368c7fd571461040b57806370a082311461041e5780637ecebe001461044757806385beb5f11461045a57600080fd5b806333e39b61116101875780635c19a95c116101565780635c19a95c146103b25780635d7b0758146103c5578063613ed6bd146103d8578063631b5dfb146103f857600080fd5b806333e39b61146103455780633644e5151461035a57806336bb2a3814610362578063395093511461039f57600080fd5b806323b872dd116101c357806323b872dd1461023d5780632aceb534146102505780632d0dd68614610301578063313ce5671461031457600080fd5b806306fdde03146101ea578063095ea7b31461020857806318160ddd1461022b575b600080fd5b6101f26105b4565b6040516101ff9190613369565b60405180910390f35b61021b6102163660046131c5565b610646565b60405190151581526020016101ff565b6002545b6040519081526020016101ff565b61021b61024b366004612fae565b61065d565b6102c961025e366004612f60565b6040805160608082018352600080835260208084018290529284018190526001600160a01b03949094168452600682529282902082519384018352546001600160d01b038116845262ffffff600160d01b8204811692850192909252600160e81b9004169082015290565b6040805182516001600160d01b0316815260208084015162ffffff9081169183019190915292820151909216908201526060016101ff565b61022f61030f36600461330a565b610723565b60405160ff7f00000000000000000000000000000000000000000000000000000000000000001681526020016101ff565b610358610353366004612f7b565b61076f565b005b61022f6107f5565b610375610370366004613187565b610804565b6040805182516001600160e01b0316815260209283015163ffffffff1692810192909252016101ff565b61021b6103ad3660046131c5565b61087c565b6103586103c0366004612f60565b6108b8565b6103586103d33660046131c5565b6108c5565b6103eb6103e63660046130b3565b610947565b6040516101ff9190613325565b610358610406366004612fae565b610a63565b6103eb610419366004613106565b610b3c565b61022f61042c366004612f60565b6001600160a01b031660009081526020819052604090205490565b61022f610455366004612f60565b610b6e565b6103eb61046836600461325c565b610b8c565b61049c61047b366004612f60565b6001600160a01b039081166000908152630100000760205260409020541690565b6040516001600160a01b0390911681526020016101ff565b6103eb6104c236600461329e565b610c6f565b6103586104d53660046131c5565b610c8a565b6103586104e8366004613054565b610d0c565b6101f2610e8c565b61022f610503366004613219565b610e9b565b61022f6105163660046131ef565b610f0c565b61021b6105293660046131c5565b610f73565b61021b61053c3660046131c5565b611024565b61035861054f366004612fea565b611031565b61022f610562366004612f7b565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b61049c7f000000000000000000000000000000000000000000000000000000000000000081565b6060600380546105c390613526565b80601f01602080910402602001604051908101604052809291908181526020018280546105ef90613526565b801561063c5780601f106106115761010080835404028352916020019161063c565b820191906000526020600020905b81548152906001019060200180831161061f57829003601f168201915b5050505050905090565b6000610653338484611195565b5060015b92915050565b600061066a8484846112ed565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156107095760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206160448201527f6c6c6f77616e636500000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6107168533858403611195565b60019150505b9392505050565b604080516060810182526007546001600160d01b038116825262ffffff600160d01b820481166020840152600160e81b9091041691810191909152600090610657906008908442611511565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146107e75760405162461bcd60e51b815260206004820152601f60248201527f436f6e74726f6c6c6564546f6b656e2f6f6e6c792d636f6e74726f6c6c6572006044820152606401610700565b6107f1828261153d565b5050565b60006107ff611613565b905090565b60408051808201909152600080825260208201526001600160a01b038316600090815260066020526040902060010161ffff831662ffffff811061084a5761084a6135ea565b604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff1660208201529392505050565b3360008181526001602090815260408083206001600160a01b038716845290915281205490916106539185906108b3908690613429565b611195565b6108c2338261153d565b50565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461093d5760405162461bcd60e51b815260206004820152601f60248201527f436f6e74726f6c6c6564546f6b656e2f6f6e6c792d636f6e74726f6c6c6572006044820152606401610700565b6107f18282611706565b60608160008167ffffffffffffffff81111561096557610965613600565b60405190808252806020026020018201604052801561098e578160200160208202803683370190505b506001600160a01b0387166000908152600660209081526040808320815160608101835281546001600160d01b038116825262ffffff600160d01b8204811695830195909552600160e81b900490931691830191909152929350905b84811015610a5657610a2783600101838a8a85818110610a0c57610a0c6135ea565b9050602002016020810190610a21919061330a565b42611511565b848281518110610a3957610a396135ea565b602090810291909101015280610a4e8161355b565b9150506109ea565b5091979650505050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610adb5760405162461bcd60e51b815260206004820152601f60248201527f436f6e74726f6c6c6564546f6b656e2f6f6e6c792d636f6e74726f6c6c6572006044820152606401610700565b816001600160a01b0316836001600160a01b031614610b2d576001600160a01b03828116600090815260016020908152604080832093871683529290522054610b2d90839085906108b39085906134f2565b610b3782826117f1565b505050565b6001600160a01b0385166000908152600660205260409020606090610b649086868686611982565b9695505050505050565b6001600160a01b038116600090815260056020526040812054610657565b60608160008167ffffffffffffffff811115610baa57610baa613600565b604051908082528060200260200182016040528015610bd3578160200160208202803683370190505b50604080516060810182526007546001600160d01b038116825262ffffff600160d01b820481166020840152600160e81b909104169181019190915290915060005b83811015610c6457610c35600883898985818110610a0c57610a0c6135ea565b838281518110610c4757610c476135ea565b602090810291909101015280610c5c8161355b565b915050610c15565b509095945050505050565b6060610c7f600786868686611982565b90505b949350505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610d025760405162461bcd60e51b815260206004820152601f60248201527f436f6e74726f6c6c6564546f6b656e2f6f6e6c792d636f6e74726f6c6c6572006044820152606401610700565b6107f182826117f1565b83421115610d5c5760405162461bcd60e51b815260206004820181905260248201527f5469636b65742f64656c65676174652d657870697265642d646561646c696e656044820152606401610700565b60007f00000000000000000000000000000000000000000000000000000000000000008787610d8a8a611b21565b6040805160208101959095526001600160a01b039384169085015291166060830152608082015260a0810186905260c0016040516020818303038152906040528051906020012090506000610dde82611b49565b90506000610dee82878787611bb2565b9050886001600160a01b0316816001600160a01b031614610e775760405162461bcd60e51b815260206004820152602160248201527f5469636b65742f64656c65676174652d696e76616c69642d7369676e6174757260448201527f65000000000000000000000000000000000000000000000000000000000000006064820152608401610700565b610e81898961153d565b505050505050505050565b6060600480546105c390613526565b6001600160a01b0383166000908152600660209081526040808320815160608101835281546001600160d01b038116825262ffffff600160d01b8204811695830195909552600160e81b90049093169183019190915290610f03906001830190868642611bda565b95945050505050565b6001600160a01b0382166000908152600660209081526040808320815160608101835281546001600160d01b038116825262ffffff600160d01b8204811695830195909552600160e81b90049093169183019190915290610c829060018301908542611511565b3360009081526001602090815260408083206001600160a01b03861684529091528120548281101561100d5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610700565b61101a3385858403611195565b5060019392505050565b60006106533384846112ed565b834211156110815760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610700565b60007f00000000000000000000000000000000000000000000000000000000000000008888886110b08c611b21565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061110b82611b49565b9050600061111b82878787611bb2565b9050896001600160a01b0316816001600160a01b03161461117e5760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610700565b6111898a8a8a611195565b50505050505050505050565b6001600160a01b0383166112105760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b03821661128c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166113695760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b0382166113e55760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610700565b6113f0838383611c12565b6001600160a01b0383166000908152602081905260409020548181101561147f5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906114b6908490613429565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161150291815260200190565b60405180910390a35b50505050565b6000808263ffffffff168463ffffffff161161152d578361152f565b825b9050610b6486868386611ca5565b6001600160a01b038281166000908152602081815260408083205463010000079092529091205490919081169083168114156115795750505050565b6001600160a01b03848116600090815263010000076020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169185169190911790556115cd818484611dbe565b826001600160a01b0316846001600160a01b03167f4bc154dd35d6a5cb9206482ecb473cdbf2473006d6bce728b9cc0741bcc59ea260405160405180910390a350505050565b60007f000000000000000000000000000000000000000000000000000000000000000046141561166257507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b6001600160a01b03821661175c5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610700565b61176860008383611c12565b806002600082825461177a9190613429565b90915550506001600160a01b038216600090815260208190526040812080548392906117a7908490613429565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6001600160a01b03821661186d5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610700565b61187982600083611c12565b6001600160a01b038216600090815260208190526040902054818110156119085760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b03831660009081526020819052604081208383039055600280548492906119379084906134f2565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b6060838281146119fa5760405162461bcd60e51b815260206004820152602360248201527f5469636b65742f73746172742d656e642d74696d65732d6c656e6774682d6d6160448201527f74636800000000000000000000000000000000000000000000000000000000006064820152608401610700565b6040805160608101825288546001600160d01b038116825262ffffff600160d01b820481166020840152600160e81b909104169181019190915260008267ffffffffffffffff811115611a4f57611a4f613600565b604051908082528060200260200182016040528015611a78578160200160208202803683370190505b5090504260005b84811015611b1257611ae38b600101858c8c85818110611aa157611aa16135ea565b9050602002016020810190611ab6919061330a565b8b8b86818110611ac857611ac86135ea565b9050602002016020810190611add919061330a565b86611bda565b838281518110611af557611af56135ea565b602090810291909101015280611b0a8161355b565b915050611a7f565b50909998505050505050505050565b6001600160a01b03811660009081526005602052604090208054600181018255905b50919050565b6000610657611b56611613565b836040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b6000806000611bc387878787611e1e565b91509150611bd081611f0b565b5095945050505050565b6000808263ffffffff168463ffffffff1611611bf65783611bf8565b825b9050611c0787878784876120fc565b979650505050505050565b816001600160a01b0316836001600160a01b03161415611c3157505050565b60006001600160a01b03841615611c6257506001600160a01b03808416600090815263010000076020526040902054165b60006001600160a01b03841615611c9357506001600160a01b03808416600090815263010000076020526040902054165b611c9e828285611dbe565b5050505050565b604080518082019091526000808252602082018190529081906040805180820190915260008082526020820152611cdc8888612198565b60208101519194509150611cfd9063ffffffff908116908890889061221816565b15611d1857505084516001600160d01b03169150610c829050565b6000611d2489896122e9565b6020810151909350909150611d459063ffffffff808a169190899061236616565b15611d57576000945050505050610c82565b611d698985838a8c604001518b612435565b8094508193505050611d848360200151836020015188612602565b63ffffffff1682600001518460000151611d9e91906134ca565b611da89190613461565b6001600160e01b03169998505050505050505050565b6001600160a01b03831615611dee57611dd783826126cc565b6001600160a01b038216611dee57611dee81612821565b6001600160a01b03821615610b3757611e07828261293a565b6001600160a01b038316610b3757610b3781612971565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611e555750600090506003611f02565b8460ff16601b14158015611e6d57508460ff16601c14155b15611e7e5750600090506004611f02565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611ed2573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611efb57600060019250925050611f02565b9150600090505b94509492505050565b6000816004811115611f1f57611f1f6135d4565b1415611f285750565b6001816004811115611f3c57611f3c6135d4565b1415611f8a5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610700565b6002816004811115611f9e57611f9e6135d4565b1415611fec5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610700565b6003816004811115612000576120006135d4565b14156120745760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610700565b6004816004811115612088576120886135d4565b14156108c25760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610700565b600080600061210b88886122e9565b9150915060008061211c8a8a612198565b9150915060006121328b8b8487878a8f8e61298c565b905060006121468c8c8588888b8f8f61298c565b905061215b816020015183602001518a612602565b63ffffffff168260000151826000015161217591906134ca565b61217f9190613461565b6001600160e01b03169c9b505050505050505050505050565b60408051808201909152600080825260208201819052906121c7836020015162ffffff1662ffffff8016612ad6565b9150838262ffffff1662ffffff81106121e2576121e26135ea565b604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff166020820152919491935090915050565b60008163ffffffff168463ffffffff161115801561224257508163ffffffff168363ffffffff1611155b1561225e578263ffffffff168463ffffffff161115905061071c565b60008263ffffffff168563ffffffff161161228d5761228863ffffffff8616640100000000613441565b612295565b8463ffffffff165b64ffffffffff16905060008363ffffffff168563ffffffff16116122cd576122c863ffffffff8616640100000000613441565b6122d5565b8463ffffffff165b64ffffffffff169091111595945050505050565b604080518082019091526000808252602082018190529082602001519150838262ffffff1662ffffff8110612320576123206135ea565b604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff166020820181905290915061235f576000915083826121e2565b9250929050565b60008163ffffffff168463ffffffff161115801561239057508163ffffffff168363ffffffff1611155b156123ab578263ffffffff168463ffffffff1610905061071c565b60008263ffffffff168563ffffffff16116123da576123d563ffffffff8616640100000000613441565b6123e2565b8463ffffffff165b64ffffffffff16905060008363ffffffff168563ffffffff161161241a5761241563ffffffff8616640100000000613441565b612422565b8463ffffffff165b64ffffffffff1690911095945050505050565b6040805180820190915260008082526020820152604080518082019091526000808252602082015260008662ffffff1690506000818962ffffff1610612480578862ffffff1661249b565b600161249162ffffff881684613429565b61249b91906134f2565b905060005b60026124ac8385613429565b6124b69190613487565b90508a6124c8828962ffffff16612b00565b62ffffff1662ffffff81106124df576124df6135ea565b604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff1660208201819052909550806125275761251f826001613429565b9350506124a0565b8b612537838a62ffffff16612b0c565b62ffffff1662ffffff811061254e5761254e6135ea565b604080518082019091529101546001600160e01b038116825263ffffffff600160e01b9091048116602083015290955060009061259390838116908c908b9061221816565b90508080156125bc57506125bc8660200151898c63ffffffff166122189092919063ffffffff16565b156125c85750506125f4565b806125df576125d86001846134f2565b93506125ed565b6125ea836001613429565b94505b50506124a0565b505050965096945050505050565b60008163ffffffff168463ffffffff161115801561262c57508163ffffffff168363ffffffff1611155b156126425761263b8385613509565b905061071c565b60008263ffffffff168563ffffffff16116126715761266c63ffffffff8616640100000000613441565b612679565b8463ffffffff165b64ffffffffff16905060008363ffffffff168563ffffffff16116126b1576126ac63ffffffff8616640100000000613441565b6126b9565b8463ffffffff165b64ffffffffff169050610b6481836134f2565b806126d5575050565b6001600160a01b0382166000908152600660205260408120908080612739846126fd87612b1c565b6040518060400160405280601b81526020017f5469636b65742f747761622d6275726e2d6c742d62616c616e6365000000000081525042612b9f565b82518754602085015160408601516001600160d01b039093167fffffff000000000000000000000000000000000000000000000000000000000090921691909117600160d01b62ffffff92831602177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160e81b9190921602178755919450925090508015612819576040805183516001600160e01b0316815260208085015163ffffffff16908201526001600160a01b038816917fdd3e7cd3a260a292b0b3306b2ca62f30a7349619a9d09c58109318774c6b627d910160405180910390a25b505050505050565b806128295750565b600080600061285b600761283c86612b1c565b6040518060600160405280602c8152602001613617602c913942612b9f565b825160078054602086015160408701516001600160d01b039094167fffffff000000000000000000000000000000000000000000000000000000000090921691909117600160d01b62ffffff92831602177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160e81b919093160291909117905591945092509050801561150b576040805183516001600160e01b0316815260208085015163ffffffff16908201527f3375b905d617084fa6b7531688cc8046feb1f1a0b8ba2273de03c59d8d84416c910160405180910390a150505050565b80612943575050565b6001600160a01b03821660009081526006602052604081209080806127398461296b87612b1c565b42612c63565b806129795750565b600080600061285b600761296b86612b1c565b60408051808201909152600080825260208201526129bf8383896020015163ffffffff166123669092919063ffffffff16565b156129e3576129dc8789600001516001600160d01b031685612d0c565b9050612aca565b8263ffffffff16876020015163ffffffff161415612a02575085612aca565b8263ffffffff16866020015163ffffffff161415612a21575084612aca565b612a408660200151838563ffffffff166123669092919063ffffffff16565b15612a655750604080518082019091526000815263ffffffff83166020820152612aca565b600080612a7a8b8888888e6040015189612435565b915091506000612a938260200151846020015187612602565b63ffffffff1683600001518360000151612aad91906134ca565b612ab79190613461565b9050612ac4838288612d0c565b93505050505b98975050505050505050565b600081612ae557506000610657565b61071c6001612af48486613429565b612afe91906134f2565b835b600061071c8284613594565b600061071c612afe846001613429565b60006001600160d01b03821115612b9b5760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203260448201527f30382062697473000000000000000000000000000000000000000000000000006064820152608401610700565b5090565b604080516060810182526000808252602082018190529181019190915260408051808201909152600080825260208201526040805160608101825287546001600160d01b0380821680845262ffffff600160d01b840481166020860152600160e81b9093049092169383019390935260009287919089161115612c355760405162461bcd60e51b81526004016107009190613369565b50612c44886001018287612d87565b8251999099036001600160d01b03168252909990985095505050505050565b604080516060810182526000808252602082018190529181019190915260408051808201909152600080825260208201526040805160608101825286546001600160d01b038116825262ffffff600160d01b820481166020840152600160e81b9091041691810191909152600090612cdf600188018287612d87565b83519296509094509250612cf49087906133be565b6001600160d01b031684525091959094509092509050565b60408051808201909152600080825260208201526040518060400160405280612d4a8660200151858663ffffffff166126029092919063ffffffff16565b612d5a9063ffffffff168661349b565b8651612d6691906133e9565b6001600160e01b031681526020018363ffffffff1681525090509392505050565b60408051606081018252600080825260208201819052918101919091526040805180820190915260008082526020820152600080612dc58787612198565b9150508463ffffffff16816020015163ffffffff161415612dee57859350915060009050612e65565b6000612e088288600001516001600160d01b031688612d0c565b90508088886020015162ffffff1662ffffff8110612e2857612e286135ea565b825160209093015163ffffffff16600160e01b026001600160e01b03909316929092179101556000612e5988612e6e565b95509093506001925050505b93509350939050565b60408051606081018252600080825260208083018290529282015290820151612e9e9062ffffff90811690612b0c565b62ffffff9081166020840152604083015181161015612b9b57600182604001818151612eca919061340b565b62ffffff169052505090565b80356001600160a01b0381168114612eed57600080fd5b919050565b60008083601f840112612f0457600080fd5b50813567ffffffffffffffff811115612f1c57600080fd5b6020830191508360208260051b850101111561235f57600080fd5b803567ffffffffffffffff81168114612eed57600080fd5b803560ff81168114612eed57600080fd5b600060208284031215612f7257600080fd5b61071c82612ed6565b60008060408385031215612f8e57600080fd5b612f9783612ed6565b9150612fa560208401612ed6565b90509250929050565b600080600060608486031215612fc357600080fd5b612fcc84612ed6565b9250612fda60208501612ed6565b9150604084013590509250925092565b600080600080600080600060e0888a03121561300557600080fd5b61300e88612ed6565b965061301c60208901612ed6565b9550604088013594506060880135935061303860808901612f4f565b925060a0880135915060c0880135905092959891949750929550565b60008060008060008060c0878903121561306d57600080fd5b61307687612ed6565b955061308460208801612ed6565b94506040870135935061309960608801612f4f565b92506080870135915060a087013590509295509295509295565b6000806000604084860312156130c857600080fd5b6130d184612ed6565b9250602084013567ffffffffffffffff8111156130ed57600080fd5b6130f986828701612ef2565b9497909650939450505050565b60008060008060006060868803121561311e57600080fd5b61312786612ed6565b9450602086013567ffffffffffffffff8082111561314457600080fd5b61315089838a01612ef2565b9096509450604088013591508082111561316957600080fd5b5061317688828901612ef2565b969995985093965092949392505050565b6000806040838503121561319a57600080fd5b6131a383612ed6565b9150602083013561ffff811681146131ba57600080fd5b809150509250929050565b600080604083850312156131d857600080fd5b6131e183612ed6565b946020939093013593505050565b6000806040838503121561320257600080fd5b61320b83612ed6565b9150612fa560208401612f37565b60008060006060848603121561322e57600080fd5b61323784612ed6565b925061324560208501612f37565b915061325360408501612f37565b90509250925092565b6000806020838503121561326f57600080fd5b823567ffffffffffffffff81111561328657600080fd5b61329285828601612ef2565b90969095509350505050565b600080600080604085870312156132b457600080fd5b843567ffffffffffffffff808211156132cc57600080fd5b6132d888838901612ef2565b909650945060208701359150808211156132f157600080fd5b506132fe87828801612ef2565b95989497509550505050565b60006020828403121561331c57600080fd5b61071c82612f37565b6020808252825182820181905260009190848201906040850190845b8181101561335d57835183529284019291840191600101613341565b50909695505050505050565b600060208083528351808285015260005b818110156133965785810183015185820160400152820161337a565b818111156133a8576000604083870101525b50601f01601f1916929092016040019392505050565b60006001600160d01b038083168185168083038211156133e0576133e06135a8565b01949350505050565b60006001600160e01b038083168185168083038211156133e0576133e06135a8565b600062ffffff8083168185168083038211156133e0576133e06135a8565b6000821982111561343c5761343c6135a8565b500190565b600064ffffffffff8083168185168083038211156133e0576133e06135a8565b60006001600160e01b038084168061347b5761347b6135be565b92169190910492915050565b600082613496576134966135be565b500490565b60006001600160e01b03808316818516818304811182151516156134c1576134c16135a8565b02949350505050565b60006001600160e01b03838116908316818110156134ea576134ea6135a8565b039392505050565b600082821015613504576135046135a8565b500390565b600063ffffffff838116908316818110156134ea576134ea6135a8565b600181811c9082168061353a57607f821691505b60208210811415611b4357634e487b7160e01b600052602260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561358d5761358d6135a8565b5060010190565b6000826135a3576135a36135be565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfe5469636b65742f6275726e2d616d6f756e742d657863656564732d746f74616c2d737570706c792d74776162a2646970667358221220f974356d9d1d5deb3e424d9c06159197d81482d3efd8c0fada57d222f39740ec64736f6c63430008060033000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000120000000000000000000000002ed22f46f28dfe855be7e0145609cabe524153f000000000000000000000000000000000000000000000000000000000000000174b616368696e672043524f20506f6f6c205469636b657400000000000000000000000000000000000000000000000000000000000000000000000000000000054b6143524f000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101e55760003560e01c806368c7fd571161010f57806395d89b41116100a2578063a9059cbb11610071578063a9059cbb1461052e578063d505accf14610541578063dd62ed3e14610554578063f77c47911461058d57600080fd5b806395d89b41146104ed57806398b16f36146104f55780639ecb037014610508578063a457c2d71461051b57600080fd5b80638d22ea2a116100de5780638d22ea2a1461046d5780638e6d536a146104b457806390596dd1146104c7578063919974dc146104da57600080fd5b806368c7fd571461040b57806370a082311461041e5780637ecebe001461044757806385beb5f11461045a57600080fd5b806333e39b61116101875780635c19a95c116101565780635c19a95c146103b25780635d7b0758146103c5578063613ed6bd146103d8578063631b5dfb146103f857600080fd5b806333e39b61146103455780633644e5151461035a57806336bb2a3814610362578063395093511461039f57600080fd5b806323b872dd116101c357806323b872dd1461023d5780632aceb534146102505780632d0dd68614610301578063313ce5671461031457600080fd5b806306fdde03146101ea578063095ea7b31461020857806318160ddd1461022b575b600080fd5b6101f26105b4565b6040516101ff9190613369565b60405180910390f35b61021b6102163660046131c5565b610646565b60405190151581526020016101ff565b6002545b6040519081526020016101ff565b61021b61024b366004612fae565b61065d565b6102c961025e366004612f60565b6040805160608082018352600080835260208084018290529284018190526001600160a01b03949094168452600682529282902082519384018352546001600160d01b038116845262ffffff600160d01b8204811692850192909252600160e81b9004169082015290565b6040805182516001600160d01b0316815260208084015162ffffff9081169183019190915292820151909216908201526060016101ff565b61022f61030f36600461330a565b610723565b60405160ff7f00000000000000000000000000000000000000000000000000000000000000121681526020016101ff565b610358610353366004612f7b565b61076f565b005b61022f6107f5565b610375610370366004613187565b610804565b6040805182516001600160e01b0316815260209283015163ffffffff1692810192909252016101ff565b61021b6103ad3660046131c5565b61087c565b6103586103c0366004612f60565b6108b8565b6103586103d33660046131c5565b6108c5565b6103eb6103e63660046130b3565b610947565b6040516101ff9190613325565b610358610406366004612fae565b610a63565b6103eb610419366004613106565b610b3c565b61022f61042c366004612f60565b6001600160a01b031660009081526020819052604090205490565b61022f610455366004612f60565b610b6e565b6103eb61046836600461325c565b610b8c565b61049c61047b366004612f60565b6001600160a01b039081166000908152630100000760205260409020541690565b6040516001600160a01b0390911681526020016101ff565b6103eb6104c236600461329e565b610c6f565b6103586104d53660046131c5565b610c8a565b6103586104e8366004613054565b610d0c565b6101f2610e8c565b61022f610503366004613219565b610e9b565b61022f6105163660046131ef565b610f0c565b61021b6105293660046131c5565b610f73565b61021b61053c3660046131c5565b611024565b61035861054f366004612fea565b611031565b61022f610562366004612f7b565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b61049c7f0000000000000000000000002ed22f46f28dfe855be7e0145609cabe524153f081565b6060600380546105c390613526565b80601f01602080910402602001604051908101604052809291908181526020018280546105ef90613526565b801561063c5780601f106106115761010080835404028352916020019161063c565b820191906000526020600020905b81548152906001019060200180831161061f57829003601f168201915b5050505050905090565b6000610653338484611195565b5060015b92915050565b600061066a8484846112ed565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156107095760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206160448201527f6c6c6f77616e636500000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6107168533858403611195565b60019150505b9392505050565b604080516060810182526007546001600160d01b038116825262ffffff600160d01b820481166020840152600160e81b9091041691810191909152600090610657906008908442611511565b336001600160a01b037f0000000000000000000000002ed22f46f28dfe855be7e0145609cabe524153f016146107e75760405162461bcd60e51b815260206004820152601f60248201527f436f6e74726f6c6c6564546f6b656e2f6f6e6c792d636f6e74726f6c6c6572006044820152606401610700565b6107f1828261153d565b5050565b60006107ff611613565b905090565b60408051808201909152600080825260208201526001600160a01b038316600090815260066020526040902060010161ffff831662ffffff811061084a5761084a6135ea565b604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff1660208201529392505050565b3360008181526001602090815260408083206001600160a01b038716845290915281205490916106539185906108b3908690613429565b611195565b6108c2338261153d565b50565b336001600160a01b037f0000000000000000000000002ed22f46f28dfe855be7e0145609cabe524153f0161461093d5760405162461bcd60e51b815260206004820152601f60248201527f436f6e74726f6c6c6564546f6b656e2f6f6e6c792d636f6e74726f6c6c6572006044820152606401610700565b6107f18282611706565b60608160008167ffffffffffffffff81111561096557610965613600565b60405190808252806020026020018201604052801561098e578160200160208202803683370190505b506001600160a01b0387166000908152600660209081526040808320815160608101835281546001600160d01b038116825262ffffff600160d01b8204811695830195909552600160e81b900490931691830191909152929350905b84811015610a5657610a2783600101838a8a85818110610a0c57610a0c6135ea565b9050602002016020810190610a21919061330a565b42611511565b848281518110610a3957610a396135ea565b602090810291909101015280610a4e8161355b565b9150506109ea565b5091979650505050505050565b336001600160a01b037f0000000000000000000000002ed22f46f28dfe855be7e0145609cabe524153f01614610adb5760405162461bcd60e51b815260206004820152601f60248201527f436f6e74726f6c6c6564546f6b656e2f6f6e6c792d636f6e74726f6c6c6572006044820152606401610700565b816001600160a01b0316836001600160a01b031614610b2d576001600160a01b03828116600090815260016020908152604080832093871683529290522054610b2d90839085906108b39085906134f2565b610b3782826117f1565b505050565b6001600160a01b0385166000908152600660205260409020606090610b649086868686611982565b9695505050505050565b6001600160a01b038116600090815260056020526040812054610657565b60608160008167ffffffffffffffff811115610baa57610baa613600565b604051908082528060200260200182016040528015610bd3578160200160208202803683370190505b50604080516060810182526007546001600160d01b038116825262ffffff600160d01b820481166020840152600160e81b909104169181019190915290915060005b83811015610c6457610c35600883898985818110610a0c57610a0c6135ea565b838281518110610c4757610c476135ea565b602090810291909101015280610c5c8161355b565b915050610c15565b509095945050505050565b6060610c7f600786868686611982565b90505b949350505050565b336001600160a01b037f0000000000000000000000002ed22f46f28dfe855be7e0145609cabe524153f01614610d025760405162461bcd60e51b815260206004820152601f60248201527f436f6e74726f6c6c6564546f6b656e2f6f6e6c792d636f6e74726f6c6c6572006044820152606401610700565b6107f182826117f1565b83421115610d5c5760405162461bcd60e51b815260206004820181905260248201527f5469636b65742f64656c65676174652d657870697265642d646561646c696e656044820152606401610700565b60007f94019368dc6b2ee4ac32010c9d0081ec29874325b541829d001d22c296b5246c8787610d8a8a611b21565b6040805160208101959095526001600160a01b039384169085015291166060830152608082015260a0810186905260c0016040516020818303038152906040528051906020012090506000610dde82611b49565b90506000610dee82878787611bb2565b9050886001600160a01b0316816001600160a01b031614610e775760405162461bcd60e51b815260206004820152602160248201527f5469636b65742f64656c65676174652d696e76616c69642d7369676e6174757260448201527f65000000000000000000000000000000000000000000000000000000000000006064820152608401610700565b610e81898961153d565b505050505050505050565b6060600480546105c390613526565b6001600160a01b0383166000908152600660209081526040808320815160608101835281546001600160d01b038116825262ffffff600160d01b8204811695830195909552600160e81b90049093169183019190915290610f03906001830190868642611bda565b95945050505050565b6001600160a01b0382166000908152600660209081526040808320815160608101835281546001600160d01b038116825262ffffff600160d01b8204811695830195909552600160e81b90049093169183019190915290610c829060018301908542611511565b3360009081526001602090815260408083206001600160a01b03861684529091528120548281101561100d5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610700565b61101a3385858403611195565b5060019392505050565b60006106533384846112ed565b834211156110815760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610700565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886110b08c611b21565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061110b82611b49565b9050600061111b82878787611bb2565b9050896001600160a01b0316816001600160a01b03161461117e5760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610700565b6111898a8a8a611195565b50505050505050505050565b6001600160a01b0383166112105760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b03821661128c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166113695760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b0382166113e55760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610700565b6113f0838383611c12565b6001600160a01b0383166000908152602081905260409020548181101561147f5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906114b6908490613429565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161150291815260200190565b60405180910390a35b50505050565b6000808263ffffffff168463ffffffff161161152d578361152f565b825b9050610b6486868386611ca5565b6001600160a01b038281166000908152602081815260408083205463010000079092529091205490919081169083168114156115795750505050565b6001600160a01b03848116600090815263010000076020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169185169190911790556115cd818484611dbe565b826001600160a01b0316846001600160a01b03167f4bc154dd35d6a5cb9206482ecb473cdbf2473006d6bce728b9cc0741bcc59ea260405160405180910390a350505050565b60007f000000000000000000000000000000000000000000000000000000000000001946141561166257507f097da033a400993428a39371761617d8b894ca1f82fdd671e90c72db485ad2f590565b50604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527f4c56e52e1e1083962e8a166de35adcba6701e0a029333db5a80367db0f67e330828401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b6001600160a01b03821661175c5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610700565b61176860008383611c12565b806002600082825461177a9190613429565b90915550506001600160a01b038216600090815260208190526040812080548392906117a7908490613429565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6001600160a01b03821661186d5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610700565b61187982600083611c12565b6001600160a01b038216600090815260208190526040902054818110156119085760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610700565b6001600160a01b03831660009081526020819052604081208383039055600280548492906119379084906134f2565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b6060838281146119fa5760405162461bcd60e51b815260206004820152602360248201527f5469636b65742f73746172742d656e642d74696d65732d6c656e6774682d6d6160448201527f74636800000000000000000000000000000000000000000000000000000000006064820152608401610700565b6040805160608101825288546001600160d01b038116825262ffffff600160d01b820481166020840152600160e81b909104169181019190915260008267ffffffffffffffff811115611a4f57611a4f613600565b604051908082528060200260200182016040528015611a78578160200160208202803683370190505b5090504260005b84811015611b1257611ae38b600101858c8c85818110611aa157611aa16135ea565b9050602002016020810190611ab6919061330a565b8b8b86818110611ac857611ac86135ea565b9050602002016020810190611add919061330a565b86611bda565b838281518110611af557611af56135ea565b602090810291909101015280611b0a8161355b565b915050611a7f565b50909998505050505050505050565b6001600160a01b03811660009081526005602052604090208054600181018255905b50919050565b6000610657611b56611613565b836040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b6000806000611bc387878787611e1e565b91509150611bd081611f0b565b5095945050505050565b6000808263ffffffff168463ffffffff1611611bf65783611bf8565b825b9050611c0787878784876120fc565b979650505050505050565b816001600160a01b0316836001600160a01b03161415611c3157505050565b60006001600160a01b03841615611c6257506001600160a01b03808416600090815263010000076020526040902054165b60006001600160a01b03841615611c9357506001600160a01b03808416600090815263010000076020526040902054165b611c9e828285611dbe565b5050505050565b604080518082019091526000808252602082018190529081906040805180820190915260008082526020820152611cdc8888612198565b60208101519194509150611cfd9063ffffffff908116908890889061221816565b15611d1857505084516001600160d01b03169150610c829050565b6000611d2489896122e9565b6020810151909350909150611d459063ffffffff808a169190899061236616565b15611d57576000945050505050610c82565b611d698985838a8c604001518b612435565b8094508193505050611d848360200151836020015188612602565b63ffffffff1682600001518460000151611d9e91906134ca565b611da89190613461565b6001600160e01b03169998505050505050505050565b6001600160a01b03831615611dee57611dd783826126cc565b6001600160a01b038216611dee57611dee81612821565b6001600160a01b03821615610b3757611e07828261293a565b6001600160a01b038316610b3757610b3781612971565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611e555750600090506003611f02565b8460ff16601b14158015611e6d57508460ff16601c14155b15611e7e5750600090506004611f02565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611ed2573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611efb57600060019250925050611f02565b9150600090505b94509492505050565b6000816004811115611f1f57611f1f6135d4565b1415611f285750565b6001816004811115611f3c57611f3c6135d4565b1415611f8a5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610700565b6002816004811115611f9e57611f9e6135d4565b1415611fec5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610700565b6003816004811115612000576120006135d4565b14156120745760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610700565b6004816004811115612088576120886135d4565b14156108c25760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610700565b600080600061210b88886122e9565b9150915060008061211c8a8a612198565b9150915060006121328b8b8487878a8f8e61298c565b905060006121468c8c8588888b8f8f61298c565b905061215b816020015183602001518a612602565b63ffffffff168260000151826000015161217591906134ca565b61217f9190613461565b6001600160e01b03169c9b505050505050505050505050565b60408051808201909152600080825260208201819052906121c7836020015162ffffff1662ffffff8016612ad6565b9150838262ffffff1662ffffff81106121e2576121e26135ea565b604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff166020820152919491935090915050565b60008163ffffffff168463ffffffff161115801561224257508163ffffffff168363ffffffff1611155b1561225e578263ffffffff168463ffffffff161115905061071c565b60008263ffffffff168563ffffffff161161228d5761228863ffffffff8616640100000000613441565b612295565b8463ffffffff165b64ffffffffff16905060008363ffffffff168563ffffffff16116122cd576122c863ffffffff8616640100000000613441565b6122d5565b8463ffffffff165b64ffffffffff169091111595945050505050565b604080518082019091526000808252602082018190529082602001519150838262ffffff1662ffffff8110612320576123206135ea565b604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff166020820181905290915061235f576000915083826121e2565b9250929050565b60008163ffffffff168463ffffffff161115801561239057508163ffffffff168363ffffffff1611155b156123ab578263ffffffff168463ffffffff1610905061071c565b60008263ffffffff168563ffffffff16116123da576123d563ffffffff8616640100000000613441565b6123e2565b8463ffffffff165b64ffffffffff16905060008363ffffffff168563ffffffff161161241a5761241563ffffffff8616640100000000613441565b612422565b8463ffffffff165b64ffffffffff1690911095945050505050565b6040805180820190915260008082526020820152604080518082019091526000808252602082015260008662ffffff1690506000818962ffffff1610612480578862ffffff1661249b565b600161249162ffffff881684613429565b61249b91906134f2565b905060005b60026124ac8385613429565b6124b69190613487565b90508a6124c8828962ffffff16612b00565b62ffffff1662ffffff81106124df576124df6135ea565b604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff1660208201819052909550806125275761251f826001613429565b9350506124a0565b8b612537838a62ffffff16612b0c565b62ffffff1662ffffff811061254e5761254e6135ea565b604080518082019091529101546001600160e01b038116825263ffffffff600160e01b9091048116602083015290955060009061259390838116908c908b9061221816565b90508080156125bc57506125bc8660200151898c63ffffffff166122189092919063ffffffff16565b156125c85750506125f4565b806125df576125d86001846134f2565b93506125ed565b6125ea836001613429565b94505b50506124a0565b505050965096945050505050565b60008163ffffffff168463ffffffff161115801561262c57508163ffffffff168363ffffffff1611155b156126425761263b8385613509565b905061071c565b60008263ffffffff168563ffffffff16116126715761266c63ffffffff8616640100000000613441565b612679565b8463ffffffff165b64ffffffffff16905060008363ffffffff168563ffffffff16116126b1576126ac63ffffffff8616640100000000613441565b6126b9565b8463ffffffff165b64ffffffffff169050610b6481836134f2565b806126d5575050565b6001600160a01b0382166000908152600660205260408120908080612739846126fd87612b1c565b6040518060400160405280601b81526020017f5469636b65742f747761622d6275726e2d6c742d62616c616e6365000000000081525042612b9f565b82518754602085015160408601516001600160d01b039093167fffffff000000000000000000000000000000000000000000000000000000000090921691909117600160d01b62ffffff92831602177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160e81b9190921602178755919450925090508015612819576040805183516001600160e01b0316815260208085015163ffffffff16908201526001600160a01b038816917fdd3e7cd3a260a292b0b3306b2ca62f30a7349619a9d09c58109318774c6b627d910160405180910390a25b505050505050565b806128295750565b600080600061285b600761283c86612b1c565b6040518060600160405280602c8152602001613617602c913942612b9f565b825160078054602086015160408701516001600160d01b039094167fffffff000000000000000000000000000000000000000000000000000000000090921691909117600160d01b62ffffff92831602177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160e81b919093160291909117905591945092509050801561150b576040805183516001600160e01b0316815260208085015163ffffffff16908201527f3375b905d617084fa6b7531688cc8046feb1f1a0b8ba2273de03c59d8d84416c910160405180910390a150505050565b80612943575050565b6001600160a01b03821660009081526006602052604081209080806127398461296b87612b1c565b42612c63565b806129795750565b600080600061285b600761296b86612b1c565b60408051808201909152600080825260208201526129bf8383896020015163ffffffff166123669092919063ffffffff16565b156129e3576129dc8789600001516001600160d01b031685612d0c565b9050612aca565b8263ffffffff16876020015163ffffffff161415612a02575085612aca565b8263ffffffff16866020015163ffffffff161415612a21575084612aca565b612a408660200151838563ffffffff166123669092919063ffffffff16565b15612a655750604080518082019091526000815263ffffffff83166020820152612aca565b600080612a7a8b8888888e6040015189612435565b915091506000612a938260200151846020015187612602565b63ffffffff1683600001518360000151612aad91906134ca565b612ab79190613461565b9050612ac4838288612d0c565b93505050505b98975050505050505050565b600081612ae557506000610657565b61071c6001612af48486613429565b612afe91906134f2565b835b600061071c8284613594565b600061071c612afe846001613429565b60006001600160d01b03821115612b9b5760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203260448201527f30382062697473000000000000000000000000000000000000000000000000006064820152608401610700565b5090565b604080516060810182526000808252602082018190529181019190915260408051808201909152600080825260208201526040805160608101825287546001600160d01b0380821680845262ffffff600160d01b840481166020860152600160e81b9093049092169383019390935260009287919089161115612c355760405162461bcd60e51b81526004016107009190613369565b50612c44886001018287612d87565b8251999099036001600160d01b03168252909990985095505050505050565b604080516060810182526000808252602082018190529181019190915260408051808201909152600080825260208201526040805160608101825286546001600160d01b038116825262ffffff600160d01b820481166020840152600160e81b9091041691810191909152600090612cdf600188018287612d87565b83519296509094509250612cf49087906133be565b6001600160d01b031684525091959094509092509050565b60408051808201909152600080825260208201526040518060400160405280612d4a8660200151858663ffffffff166126029092919063ffffffff16565b612d5a9063ffffffff168661349b565b8651612d6691906133e9565b6001600160e01b031681526020018363ffffffff1681525090509392505050565b60408051606081018252600080825260208201819052918101919091526040805180820190915260008082526020820152600080612dc58787612198565b9150508463ffffffff16816020015163ffffffff161415612dee57859350915060009050612e65565b6000612e088288600001516001600160d01b031688612d0c565b90508088886020015162ffffff1662ffffff8110612e2857612e286135ea565b825160209093015163ffffffff16600160e01b026001600160e01b03909316929092179101556000612e5988612e6e565b95509093506001925050505b93509350939050565b60408051606081018252600080825260208083018290529282015290820151612e9e9062ffffff90811690612b0c565b62ffffff9081166020840152604083015181161015612b9b57600182604001818151612eca919061340b565b62ffffff169052505090565b80356001600160a01b0381168114612eed57600080fd5b919050565b60008083601f840112612f0457600080fd5b50813567ffffffffffffffff811115612f1c57600080fd5b6020830191508360208260051b850101111561235f57600080fd5b803567ffffffffffffffff81168114612eed57600080fd5b803560ff81168114612eed57600080fd5b600060208284031215612f7257600080fd5b61071c82612ed6565b60008060408385031215612f8e57600080fd5b612f9783612ed6565b9150612fa560208401612ed6565b90509250929050565b600080600060608486031215612fc357600080fd5b612fcc84612ed6565b9250612fda60208501612ed6565b9150604084013590509250925092565b600080600080600080600060e0888a03121561300557600080fd5b61300e88612ed6565b965061301c60208901612ed6565b9550604088013594506060880135935061303860808901612f4f565b925060a0880135915060c0880135905092959891949750929550565b60008060008060008060c0878903121561306d57600080fd5b61307687612ed6565b955061308460208801612ed6565b94506040870135935061309960608801612f4f565b92506080870135915060a087013590509295509295509295565b6000806000604084860312156130c857600080fd5b6130d184612ed6565b9250602084013567ffffffffffffffff8111156130ed57600080fd5b6130f986828701612ef2565b9497909650939450505050565b60008060008060006060868803121561311e57600080fd5b61312786612ed6565b9450602086013567ffffffffffffffff8082111561314457600080fd5b61315089838a01612ef2565b9096509450604088013591508082111561316957600080fd5b5061317688828901612ef2565b969995985093965092949392505050565b6000806040838503121561319a57600080fd5b6131a383612ed6565b9150602083013561ffff811681146131ba57600080fd5b809150509250929050565b600080604083850312156131d857600080fd5b6131e183612ed6565b946020939093013593505050565b6000806040838503121561320257600080fd5b61320b83612ed6565b9150612fa560208401612f37565b60008060006060848603121561322e57600080fd5b61323784612ed6565b925061324560208501612f37565b915061325360408501612f37565b90509250925092565b6000806020838503121561326f57600080fd5b823567ffffffffffffffff81111561328657600080fd5b61329285828601612ef2565b90969095509350505050565b600080600080604085870312156132b457600080fd5b843567ffffffffffffffff808211156132cc57600080fd5b6132d888838901612ef2565b909650945060208701359150808211156132f157600080fd5b506132fe87828801612ef2565b95989497509550505050565b60006020828403121561331c57600080fd5b61071c82612f37565b6020808252825182820181905260009190848201906040850190845b8181101561335d57835183529284019291840191600101613341565b50909695505050505050565b600060208083528351808285015260005b818110156133965785810183015185820160400152820161337a565b818111156133a8576000604083870101525b50601f01601f1916929092016040019392505050565b60006001600160d01b038083168185168083038211156133e0576133e06135a8565b01949350505050565b60006001600160e01b038083168185168083038211156133e0576133e06135a8565b600062ffffff8083168185168083038211156133e0576133e06135a8565b6000821982111561343c5761343c6135a8565b500190565b600064ffffffffff8083168185168083038211156133e0576133e06135a8565b60006001600160e01b038084168061347b5761347b6135be565b92169190910492915050565b600082613496576134966135be565b500490565b60006001600160e01b03808316818516818304811182151516156134c1576134c16135a8565b02949350505050565b60006001600160e01b03838116908316818110156134ea576134ea6135a8565b039392505050565b600082821015613504576135046135a8565b500390565b600063ffffffff838116908316818110156134ea576134ea6135a8565b600181811c9082168061353a57607f821691505b60208210811415611b4357634e487b7160e01b600052602260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561358d5761358d6135a8565b5060010190565b6000826135a3576135a36135be565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfe5469636b65742f6275726e2d616d6f756e742d657863656564732d746f74616c2d737570706c792d74776162a2646970667358221220f974356d9d1d5deb3e424d9c06159197d81482d3efd8c0fada57d222f39740ec64736f6c63430008060033

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

000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000120000000000000000000000002ed22f46f28dfe855be7e0145609cabe524153f000000000000000000000000000000000000000000000000000000000000000174b616368696e672043524f20506f6f6c205469636b657400000000000000000000000000000000000000000000000000000000000000000000000000000000054b6143524f000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _name (string): Kaching CRO Pool Ticket
Arg [1] : _symbol (string): KaCRO
Arg [2] : decimals_ (uint8): 18
Arg [3] : _controller (address): 0x2ed22F46F28dFe855BE7E0145609cABe524153F0

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [3] : 0000000000000000000000002ed22f46f28dfe855be7e0145609cabe524153f0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000017
Arg [5] : 4b616368696e672043524f20506f6f6c205469636b6574000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [7] : 4b6143524f000000000000000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
view all blocks validated

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

Validator Index Block Amount
View All Withdrawals

Transaction 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.