false
false
0
The new Blockscout UI is now open source! Learn how to deploy it here

Contract Address Details

0xe89Ca267cd711b33411b22CF778A793Cad1e9a5A

Contract Name
TokenVesting
Creator
0xb36226–bda109 at 0x9f7496–86c091
Balance
0 ETH
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
1064473
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
TokenVesting




Optimization enabled
true
Compiler version
v0.8.18+commit.87f61d96




Optimization runs
200
EVM Version
default




Verified at
2024-04-27T12:05:56.263177Z

Constructor Arguments

0x0000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a

Arg [0] (address) : 0x0011e559da84dde3f841e22dc33f3adbf184d84a

              

contracts/TokenVesting.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.18;

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./AccessProtected.sol";

/**
@title Access Limiter to multiple owner-specified accounts.
@dev An address with role admin can:
- Can allocate tokens owned by the contract to a given recipient through a vesting agreement via the functions createGrant() (one by one) and createGrantsBatch() (batch).
- Can withdraw unallocated tokens owned by the contract via the function withdrawAdmin().
- Can revoke an active grant for a given address via the function revokeGrant().
- Can withdraw any ERC20 token owner by the contract and different than the vested token via the function withdrawOtherToken().
*/
contract TokenVesting is Context, AccessProtected, ReentrancyGuard {
	using SafeERC20 for IERC20;

	// Address of the token that we're vesting
	IERC20 public immutable tokenAddress;

	// Current total vesting allocation
	uint256 public numTokensReservedForVesting = 0;

	/**
    @notice A structure representing a Grant - supporting linear and cliff vesting.
     */
	struct Grant {
		uint40 startTimestamp;
		uint40 endTimestamp;
		uint40 cliffReleaseTimestamp;
		uint40 releaseIntervalSecs; // used for calculating the vested amount
		uint256 linearVestAmount; // vesting allocation, excluding cliff
		uint256 claimedAmount; // claimed so far, excluding cliff
		uint112 cliffAmount;
		bool isActive; // revoked if false
		uint40 deactivationTimestamp;
	}

	mapping(address => Grant) internal grants;
	address[] internal vestingRecipients;

	/**
    @notice Emitted on creation of new Grant
     */
	event GrantCreated(address indexed _recipient, Grant _grant);

	/**
    @notice Emitted on withdrawal from Grant
     */
	event Claimed(address indexed _recipient, uint256 _withdrawalAmount);

	/**
    @notice Emitted on Grant revoke
     */
	event GrantRevoked(
		address indexed _recipient,
		uint256 _numTokensWithheld,
		Grant _grant
	);

	/**
    @notice Emitted on admin withdrawal
     */
	event AdminWithdrawn(address indexed _recipient, uint256 _amountRequested);

	/**
    @notice Construct the contract, taking the ERC20 token to be vested as the parameter.
    @dev The owner can set the token in question when creating the contract.
    */
	constructor(IERC20 _tokenAddress) {
		require(address(_tokenAddress) != address(0), "INVALID_ADDRESS");
		tokenAddress = _tokenAddress;
	}

	/**
    @notice Basic getter for a Grant.
    @param _recipient - Grant recipient wallet address
     */
	function getGrant(address _recipient) external view returns (Grant memory) {
		return grants[_recipient];
	}

	/**
    @notice Check if Recipient has an active grant attached.
    @dev
    * Grant is considered active if:
    * - is active
    * - start timestamp is greater than 0
    *
    * We can use startTimestamp as a criterion because it is only assigned a value in
    * createGrant, and it is never modified afterwards. Thus, startTimestamp will have a value
    * only if a grant has been created. Moreover, we need to verify
    * that the grant is active (since this is has_Active_Grant).
    */
	modifier hasActiveGrant(address _recipient) {
		Grant memory _grant = grants[_recipient];
		require(_grant.startTimestamp > 0, "NO_ACTIVE_GRANT");

		// We however still need the active check, since (due to the name of the function)
		// we want to only allow active grants
		require(_grant.isActive, "NO_ACTIVE_GRANT");

		_;
	}

	/**
    @notice Check if the recipient has no active grant attached.
    @dev Requires that all fields are unset
    */
	modifier hasNoGrant(address _recipient) {
		Grant memory _grant = grants[_recipient];
		// A grant is only created when its start timestamp is nonzero
		// So, a zero value for the start timestamp means the grant does not exist
		require(_grant.startTimestamp == 0, "GRANT_ALREADY_EXISTS");
		_;
	}

	/**
    @notice Calculate the vested amount for a given Grant, at a given timestamp.
    @param _grant The grant in question
    @param _referenceTs Timestamp for which we're calculating
     */
	function _baseVestedAmount(
		Grant memory _grant,
		uint40 _referenceTs
	) internal pure returns (uint256) {
		// Does the Grant exist?
		if (!_grant.isActive && _grant.deactivationTimestamp == 0) {
			return 0;
		}

        uint256 vestAmt;

		// Has the Grant ended?
		if (_referenceTs > _grant.endTimestamp) {
			_referenceTs = _grant.endTimestamp;
		}

		// Has the cliff passed?
		if (_referenceTs >= _grant.cliffReleaseTimestamp) {
			vestAmt += _grant.cliffAmount;
		}

		// Has the vesting started? If so, calculate the vested amount linearly
		if (_referenceTs > _grant.startTimestamp) {
			uint40 currentVestingDurationSecs = _referenceTs -
				_grant.startTimestamp;

			// Round to releaseIntervalSecs
			uint40 truncatedCurrentVestingDurationSecs = (currentVestingDurationSecs /
					_grant.releaseIntervalSecs) * _grant.releaseIntervalSecs;

			uint40 finalVestingDurationSecs = _grant.endTimestamp -
				_grant.startTimestamp;

			// Calculate vested amount
			uint256 linearVestAmount = (_grant.linearVestAmount *
				truncatedCurrentVestingDurationSecs) / finalVestingDurationSecs;

			vestAmt += linearVestAmount;
		}

		return vestAmt;
	}

	/**
    @notice Calculate the vested amount for a given Recipient, at a given Timestamp.
    @param _recipient - Grant recipient wallet address
    @param _referenceTs - Reference date timestamp
    */
	function vestedAmount(
		address _recipient,
		uint40 _referenceTs
	) public view returns (uint256) {
		Grant memory _grant = grants[_recipient];
		uint40 vestEndTimestamp = _grant.isActive
			? _referenceTs
			: _grant.deactivationTimestamp;
		return _baseVestedAmount(_grant, vestEndTimestamp);
	}

	/**
    @notice Return total allocation for a given Recipient.
    @param _recipient - Grant recipient wallet address
     */
	function grantAllocation(address _recipient) public view returns (uint256) {
		Grant memory _grant = grants[_recipient];
		return _baseVestedAmount(_grant, _grant.endTimestamp);
	}

	/**
    @notice Currently claimable amount for a given Recipient.
    @param _recipient - Grant recipient wallet address
    */
	function claimableAmount(address _recipient) public view returns (uint256) {
		Grant memory _grant = grants[_recipient];
		return
			vestedAmount(_recipient, uint40(block.timestamp)) -
			_grant.claimedAmount;
	}

	/**
    @notice Remaining allocation for Recipient. Total allocation minus already withdrawn amount.
    @param _recipient - Grant recipient wallet address
    */
	function finalClaimableAmount(
		address _recipient
	) external view returns (uint256) {
		Grant storage _grant = grants[_recipient];
		uint40 vestEndTimestamp = _grant.isActive
			? _grant.endTimestamp
			: _grant.deactivationTimestamp;
		return
			_baseVestedAmount(_grant, vestEndTimestamp) - _grant.claimedAmount;
	}

	/**
    @notice Get all active recipients
    */
	function allVestingRecipients() external view returns (address[] memory) {
		return vestingRecipients;
	}

	/**
    @notice Get active recipients count
    */
	function numVestingRecipients() external view returns (uint256) {
		return vestingRecipients.length;
	}

	/**
    @notice Create Grant logic, called by createGrant and createGrantsBatch.
    @dev Only input validation. Does not check if the startTimestamp is in the past to allow to back-allocate.
    @param _recipient - Grant recipient wallet address
    @param _startTimestamp - Vesting start date timestamp
    @param _endTimestamp - Vesting end date timestamp
    @param _cliffReleaseTimestamp - Lump sum cliff release date timestamp. Usually equal to _startTimestamp, must be <= _startTimestamp, or 0 if no cliff
    @param _releaseIntervalSecs - Time between releases, expressed in seconds
    @param _linearVestAmount - Allocation to be linearly vested between _startTimestamp and _endTimestamp (excluding cliff)
    @param _cliffAmount - The amount released at _cliffReleaseTimestamp. Can be 0 if _cliffReleaseTimestamp is also 0.
     */
	function _createGrantUnchecked(
		address _recipient,
		uint40 _startTimestamp,
		uint40 _endTimestamp,
		uint40 _cliffReleaseTimestamp,
		uint40 _releaseIntervalSecs,
		uint112 _linearVestAmount,
		uint112 _cliffAmount
	) private hasNoGrant(_recipient) {
		require(_recipient != address(0), "INVALID_ADDRESS");
		require(_linearVestAmount + _cliffAmount > 0, "INVALID_VESTED_AMOUNT");
		require(_startTimestamp > 0, "INVALID_START_TIMESTAMP");
		require(_startTimestamp < _endTimestamp, "INVALID_END_TIMESTAMP");
		require(_releaseIntervalSecs > 0, "INVALID_RELEASE_INTERVAL");
		require(
			(_endTimestamp - _startTimestamp) % _releaseIntervalSecs == 0,
			"INVALID_INTERVAL_LENGTH"
		);

		// Both or neither of cliff parameters must be set.
		// If cliff is set, the cliff timestamp must be before or at the vesting timestamp
		require(
			(_cliffReleaseTimestamp > 0 &&
				_cliffAmount > 0 &&
				_cliffReleaseTimestamp <= _startTimestamp) ||
				(_cliffReleaseTimestamp == 0 && _cliffAmount == 0),
			"INVALID_CLIFF"
		);

		Grant storage _grant = grants[_recipient];
		_grant.startTimestamp = _startTimestamp;
		_grant.endTimestamp = _endTimestamp;
		_grant.cliffReleaseTimestamp = _cliffReleaseTimestamp;
		_grant.releaseIntervalSecs = _releaseIntervalSecs;
		_grant.linearVestAmount = _linearVestAmount;
		_grant.cliffAmount = _cliffAmount;
		_grant.isActive = true;

		uint256 allocatedAmount = _cliffAmount + _linearVestAmount;

		// Can we afford to create a new Grant?
		require(
			tokenAddress.balanceOf(address(this)) >=
				numTokensReservedForVesting + allocatedAmount,
			"INSUFFICIENT_BALANCE"
		);

		numTokensReservedForVesting += allocatedAmount;
		vestingRecipients.push(_recipient);
		emit GrantCreated(_recipient, _grant);
	}

	/**
    @notice Create a grant based on the input parameters.
    @param _recipient - Grant recipient wallet address
    @param _startTimestamp - Vesting start date timestamp
    @param _endTimestamp - Vesting end date timestamp
    @param _cliffReleaseTimestamp - Lump sum cliff release date timestamp. Usually equal to _startTimestamp, must be <= _startTimestamp, or 0 if no cliff
    @param _releaseIntervalSecs - Time between releases, expressed in seconds
    @param _linearVestAmount - Allocation to be linearly vested between _startTimestamp and _endTimestamp (excluding cliff)
    @param _cliffAmount - The amount released at _cliffReleaseTimestamp. Can be 0 if _cliffReleaseTimestamp is also 0.
     */
	function createGrant(
		address _recipient,
		uint40 _startTimestamp,
		uint40 _endTimestamp,
		uint40 _cliffReleaseTimestamp,
		uint40 _releaseIntervalSecs,
		uint112 _linearVestAmount,
		uint112 _cliffAmount
	) external onlyAdmin {
		_createGrantUnchecked(
			_recipient,
			_startTimestamp,
			_endTimestamp,
			_cliffReleaseTimestamp,
			_releaseIntervalSecs,
			_linearVestAmount,
			_cliffAmount
		);
	}

	/**
    @notice Simple for loop sequential batch create. Takes n-th element of each array to create the Grant.
        @param _recipients - Array of Grant recipient wallet address
        @param _startTimestamps - Array of vesting start date timestamps
        @param _endTimestamps - Array of vesting end date timestamps
        @param _cliffReleaseTimestamps - Array of cliff release date timestamps
        @param _releaseIntervalsSecs - Array of time intervals between releases, expressed in seconds
        @param _linearVestAmounts - Array of allocations
        @param _cliffAmounts - Array of cliff release amounts
     */
	function createGrantsBatch(
		address[] memory _recipients,
		uint40[] memory _startTimestamps,
		uint40[] memory _endTimestamps,
		uint40[] memory _cliffReleaseTimestamps,
		uint40[] memory _releaseIntervalsSecs,
		uint112[] memory _linearVestAmounts,
		uint112[] memory _cliffAmounts
	) external onlyAdmin {
		uint256 length = _recipients.length;
		require(
			_startTimestamps.length == length &&
				_endTimestamps.length == length &&
				_cliffReleaseTimestamps.length == length &&
				_releaseIntervalsSecs.length == length &&
				_linearVestAmounts.length == length &&
				_cliffAmounts.length == length,
			"ARRAY_LENGTH_MISMATCH"
		);

		for (uint256 i = 0; i < length; i++) {
			_createGrantUnchecked(
				_recipients[i],
				_startTimestamps[i],
				_endTimestamps[i],
				_cliffReleaseTimestamps[i],
				_releaseIntervalsSecs[i],
				_linearVestAmounts[i],
				_cliffAmounts[i]
			);
		}
	}

	/**
    @notice Withdraw the claimable balance. Only callable by active Grant recipients.
     */
	function claim() external nonReentrant {
		Grant storage usrGrant = grants[_msgSender()];

		uint256 vested = vestedAmount(_msgSender(), uint40(block.timestamp));

		require(
			vested > usrGrant.claimedAmount,
			"NOTHING_TO_WITHDRAW"
		);

		uint256 amountRemaining = vested - usrGrant.claimedAmount;
		require(amountRemaining > 0, "NOTHING_TO_WITHDRAW");

		usrGrant.claimedAmount += amountRemaining;
		numTokensReservedForVesting -= amountRemaining;

		// Reentrancy: internal vars have been changed by now
		tokenAddress.safeTransfer(_msgSender(), amountRemaining);

		emit Claimed(_msgSender(), amountRemaining);
	}

	/**
    @notice Allow the owner to withdraw any balance not currently tied up in Grants
    @param _amountRequested - Amount to withdraw
     */
	function withdrawAdmin(
		uint256 _amountRequested
	) public onlyAdmin nonReentrant {
		uint256 amountRemaining = amountAvailableToWithdrawByAdmin();
		require(amountRemaining >= _amountRequested, "INSUFFICIENT_BALANCE");

		// Reentrancy: No changes to internal vars, only transfer
		tokenAddress.safeTransfer(_msgSender(), _amountRequested);

		emit AdminWithdrawn(_msgSender(), _amountRequested);
	}

	/**
    @notice Revoke active Grant. Grant must exist and be active.
    @param _recipient - Grant recipient wallet address
    */
	function revokeGrant(
		address _recipient
	) external onlyAdmin hasActiveGrant(_recipient) {
		Grant storage _grant = grants[_recipient];
		uint256 finalVestAmt = grantAllocation(_recipient);

		require(_grant.claimedAmount < finalVestAmt, "NO_UNVESTED_AMOUNT");

		_grant.isActive = false;
		_grant.deactivationTimestamp = uint40(block.timestamp);

		uint256 vestedSoFarAmt = vestedAmount(
			_recipient,
			uint40(block.timestamp)
		);
		uint256 amountRemaining = finalVestAmt - vestedSoFarAmt;
		numTokensReservedForVesting -= amountRemaining;

		emit GrantRevoked(
			_recipient,
			amountRemaining,
			_grant
		);
	}

	/**
    @notice Withdraw a token which isn't controlled by the vesting contract. Useful when someone accidentally sends tokens to the contract
    that arent the token that the contract is configured vest (tokenAddress).
    @param _otherTokenAddress - the token which we want to withdraw
     */
	function withdrawOtherToken(
		IERC20 _otherTokenAddress
	) external onlyAdmin nonReentrant {
		require(_otherTokenAddress != tokenAddress, "INVALID_TOKEN"); // tokenAddress address is already sure to be nonzero due to constructor
		uint256 balance = _otherTokenAddress.balanceOf(address(this));
		require(balance > 0, "INSUFFICIENT_BALANCE");
		_otherTokenAddress.safeTransfer(_msgSender(), balance);
	}

	/**
	 * @notice How many tokens are available to withdraw by the admin.
	 */
	function amountAvailableToWithdrawByAdmin() public view returns (uint256) {
		return
			tokenAddress.balanceOf(address(this)) - numTokensReservedForVesting;
	}
}
        

@openzeppelin/contracts/access/Ownable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

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

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

@openzeppelin/contracts/interfaces/IERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";
          

@openzeppelin/contracts/security/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

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

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

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

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

@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

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");
        }
    }
}
          

@openzeppelin/contracts/utils/Address.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 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);
            }
        }
    }
}
          

@openzeppelin/contracts/utils/Context.sol

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

pragma solidity ^0.8.0;

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

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

contracts/AccessProtected.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.18;

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/** 
@title Access Limiter to multiple owner-specified accounts.
@dev Exposes the onlyAdmin modifier, which will revert (ADMIN_ACCESS_REQUIRED) if the caller is not the owner nor the admin.
@notice An address with the role admin can grant that role to or revoke that role from any address via the function setAdmin().
*/
abstract contract AccessProtected is Context {
    mapping(address => bool) private _admins; // user address => admin? mapping
    uint public adminCount;

    event AdminAccessSet(address indexed _admin, bool _enabled);

    constructor() {
        _admins[_msgSender()] = true;
        adminCount = 1;
        emit AdminAccessSet(_msgSender(), true);
    }

    /**
     * Throws if called by any account that isn't an admin or an owner.
     */
    modifier onlyAdmin() {
        require(_admins[_msgSender()], "ADMIN_ACCESS_REQUIRED");
        _;
    }

    function isAdmin(address _addressToCheck) external view returns (bool) {
        return _admins[_addressToCheck];
    }

    /**
     * @notice Set/unset Admin Access for a given address.
     *
     * @param admin - Address of the new admin (or the one to be removed)
     * @param isEnabled - Enable/Disable Admin Access
     */
    function setAdmin(address admin, bool isEnabled) public onlyAdmin {
        require(admin != address(0), "INVALID_ADDRESS");
        require(_admins[admin] != isEnabled, "FLAG_ALREADY_PRESENT_FOR_ADDRESS");

        if (isEnabled) {
            adminCount++;
        } else {
            require(adminCount > 1, "AT_LEAST_ONE_ADMIN_REQUIRED");
            adminCount--;
        }

        _admins[admin] = isEnabled;
        emit AdminAccessSet(admin, isEnabled);
    }
}
          

Compiler Settings

{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{}}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_tokenAddress","internalType":"contract IERC20"}]},{"type":"event","name":"AdminAccessSet","inputs":[{"type":"address","name":"_admin","internalType":"address","indexed":true},{"type":"bool","name":"_enabled","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"AdminWithdrawn","inputs":[{"type":"address","name":"_recipient","internalType":"address","indexed":true},{"type":"uint256","name":"_amountRequested","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Claimed","inputs":[{"type":"address","name":"_recipient","internalType":"address","indexed":true},{"type":"uint256","name":"_withdrawalAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"GrantCreated","inputs":[{"type":"address","name":"_recipient","internalType":"address","indexed":true},{"type":"tuple","name":"_grant","internalType":"struct TokenVesting.Grant","indexed":false,"components":[{"type":"uint40","name":"startTimestamp","internalType":"uint40"},{"type":"uint40","name":"endTimestamp","internalType":"uint40"},{"type":"uint40","name":"cliffReleaseTimestamp","internalType":"uint40"},{"type":"uint40","name":"releaseIntervalSecs","internalType":"uint40"},{"type":"uint256","name":"linearVestAmount","internalType":"uint256"},{"type":"uint256","name":"claimedAmount","internalType":"uint256"},{"type":"uint112","name":"cliffAmount","internalType":"uint112"},{"type":"bool","name":"isActive","internalType":"bool"},{"type":"uint40","name":"deactivationTimestamp","internalType":"uint40"}]}],"anonymous":false},{"type":"event","name":"GrantRevoked","inputs":[{"type":"address","name":"_recipient","internalType":"address","indexed":true},{"type":"uint256","name":"_numTokensWithheld","internalType":"uint256","indexed":false},{"type":"tuple","name":"_grant","internalType":"struct TokenVesting.Grant","indexed":false,"components":[{"type":"uint40","name":"startTimestamp","internalType":"uint40"},{"type":"uint40","name":"endTimestamp","internalType":"uint40"},{"type":"uint40","name":"cliffReleaseTimestamp","internalType":"uint40"},{"type":"uint40","name":"releaseIntervalSecs","internalType":"uint40"},{"type":"uint256","name":"linearVestAmount","internalType":"uint256"},{"type":"uint256","name":"claimedAmount","internalType":"uint256"},{"type":"uint112","name":"cliffAmount","internalType":"uint112"},{"type":"bool","name":"isActive","internalType":"bool"},{"type":"uint40","name":"deactivationTimestamp","internalType":"uint40"}]}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"adminCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"allVestingRecipients","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"amountAvailableToWithdrawByAdmin","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claim","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"claimableAmount","inputs":[{"type":"address","name":"_recipient","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createGrant","inputs":[{"type":"address","name":"_recipient","internalType":"address"},{"type":"uint40","name":"_startTimestamp","internalType":"uint40"},{"type":"uint40","name":"_endTimestamp","internalType":"uint40"},{"type":"uint40","name":"_cliffReleaseTimestamp","internalType":"uint40"},{"type":"uint40","name":"_releaseIntervalSecs","internalType":"uint40"},{"type":"uint112","name":"_linearVestAmount","internalType":"uint112"},{"type":"uint112","name":"_cliffAmount","internalType":"uint112"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createGrantsBatch","inputs":[{"type":"address[]","name":"_recipients","internalType":"address[]"},{"type":"uint40[]","name":"_startTimestamps","internalType":"uint40[]"},{"type":"uint40[]","name":"_endTimestamps","internalType":"uint40[]"},{"type":"uint40[]","name":"_cliffReleaseTimestamps","internalType":"uint40[]"},{"type":"uint40[]","name":"_releaseIntervalsSecs","internalType":"uint40[]"},{"type":"uint112[]","name":"_linearVestAmounts","internalType":"uint112[]"},{"type":"uint112[]","name":"_cliffAmounts","internalType":"uint112[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"finalClaimableAmount","inputs":[{"type":"address","name":"_recipient","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct TokenVesting.Grant","components":[{"type":"uint40","name":"startTimestamp","internalType":"uint40"},{"type":"uint40","name":"endTimestamp","internalType":"uint40"},{"type":"uint40","name":"cliffReleaseTimestamp","internalType":"uint40"},{"type":"uint40","name":"releaseIntervalSecs","internalType":"uint40"},{"type":"uint256","name":"linearVestAmount","internalType":"uint256"},{"type":"uint256","name":"claimedAmount","internalType":"uint256"},{"type":"uint112","name":"cliffAmount","internalType":"uint112"},{"type":"bool","name":"isActive","internalType":"bool"},{"type":"uint40","name":"deactivationTimestamp","internalType":"uint40"}]}],"name":"getGrant","inputs":[{"type":"address","name":"_recipient","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"grantAllocation","inputs":[{"type":"address","name":"_recipient","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isAdmin","inputs":[{"type":"address","name":"_addressToCheck","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"numTokensReservedForVesting","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"numVestingRecipients","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeGrant","inputs":[{"type":"address","name":"_recipient","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAdmin","inputs":[{"type":"address","name":"admin","internalType":"address"},{"type":"bool","name":"isEnabled","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"tokenAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"vestedAmount","inputs":[{"type":"address","name":"_recipient","internalType":"address"},{"type":"uint40","name":"_referenceTs","internalType":"uint40"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawAdmin","inputs":[{"type":"uint256","name":"_amountRequested","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawOtherToken","inputs":[{"type":"address","name":"_otherTokenAddress","internalType":"contract IERC20"}]}]
              

Contract Creation Code

0x60a060405260006003553480156200001657600080fd5b5060405162002559380380620025598339810160408190526200003991620000ef565b3360008181526020818152604091829020805460ff1916600190811790915580805591519182527fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb78910160405180910390a260016002556001600160a01b038116620000dd5760405162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b604482015260640160405180910390fd5b6001600160a01b031660805262000121565b6000602082840312156200010257600080fd5b81516001600160a01b03811681146200011a57600080fd5b9392505050565b6080516123f9620001606000396000818161022501528181610c5001528181610e6401528181610f2f015281816110a501526117c501526123f96000f3fe608060405234801561001057600080fd5b50600436106101215760003560e01c80634e71d92d116100ad578063d77836ce11610071578063d77836ce1461036d578063e50373f914610380578063e7b1d1a414610393578063e865fbc7146103a6578063f37d94b4146103ae57600080fd5b80634e71d92d146101f05780637197dec3146101f8578063898850491461020d5780639d76ea5814610220578063bf31dbf41461025f57600080fd5b806324d7806c116100f457806324d7806c146101725780632b7832b3146101ae5780634a0e701e146101b75780634b0bddd2146101ca5780634b2429d7146101dd57600080fd5b8063137c68fa146101265780631817c5a7146101425780631e2f64a614610157578063241c5b1f1461016a575b600080fd5b61012f60035481565b6040519081526020015b60405180910390f35b610155610150366004611b55565b6103c1565b005b61012f610165366004611b55565b610641565b60055461012f565b61019e610180366004611b55565b6001600160a01b031660009081526020819052604090205460ff1690565b6040519015158152602001610139565b61012f60015481565b61012f6101c5366004611b8c565b6106fb565b6101556101d8366004611bcf565b6107ce565b6101556101eb366004611dc2565b610999565b610155610b34565b610200610cbe565b6040516101399190611edd565b61012f61021b366004611b55565b610d20565b6102477f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610139565b61036061026d366004611b55565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810191909152506001600160a01b0316600090815260046020908152604091829020825161012081018452815464ffffffffff8082168352600160281b8204811694830194909452600160501b8104841694820194909452600160781b938490048316606082015260018201546080820152600282015460a08201526003909101546001600160701b03811660c083015260ff600160701b820416151560e0830152929092041661010082015290565b6040516101399190611f2a565b61015561037b366004611fc2565b610ddf565b61015561038e366004611b55565b610ed1565b6101556103a1366004611fdb565b61103a565b61012f611081565b61012f6103bc366004611b55565b61111f565b3360009081526020819052604090205460ff166103f95760405162461bcd60e51b81526004016103f090612063565b60405180910390fd5b6001600160a01b038116600090815260046020908152604091829020825161012081018452815464ffffffffff808216808452600160281b8304821695840195909552600160501b8204811695830195909552600160781b908190048516606083015260018301546080830152600283015460a08301526003909201546001600160701b03811660c083015260ff600160701b820416151560e0830152919091049092166101008301528291906104e45760405162461bcd60e51b815260206004820152600f60248201526e1393d7d050d512559157d1d4905395608a1b60448201526064016103f0565b8060e001516105275760405162461bcd60e51b815260206004820152600f60248201526e1393d7d050d512559157d1d4905395608a1b60448201526064016103f0565b6001600160a01b03831660009081526004602052604081209061054985610641565b9050808260020154106105935760405162461bcd60e51b81526020600482015260126024820152711393d7d553959154d5115117d05353d5539560721b60448201526064016103f0565b60038201805464ffffffffff42908116600160781b0265ffffffffffff60701b19909216919091179091556000906105cc9087906106fb565b905060006105da82846120a8565b905080600360008282546105ee91906120a8565b92505081905550866001600160a01b03167f0995a2a9d2c36811626ef62e0415f97a104f1c7e7285637e86a20df2f3a4aa598286604051610630929190612149565b60405180910390a250505050505050565b6001600160a01b0381166000908152600460209081526040808320815161012081018352815464ffffffffff8082168352600160281b82048116958301869052600160501b8204811694830194909452600160781b908190048416606083015260018301546080830152600283015460a08301526003909201546001600160701b03811660c083015260ff600160701b820416151560e083015291909104909116610100820152906106f4908290611220565b9392505050565b6001600160a01b0382166000908152600460209081526040808320815161012081018352815464ffffffffff8082168352600160281b8204811695830195909552600160501b8104851693820193909352600160781b928390048416606082015260018201546080820152600282015460a08201526003909101546001600160701b03811660c083015260ff600160701b820416151560e0830181905292900490921661010083015282906107b5578161010001516107b7565b835b90506107c38282611220565b925050505b92915050565b3360009081526020819052604090205460ff166107fd5760405162461bcd60e51b81526004016103f090612063565b6001600160a01b0382166108455760405162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b60448201526064016103f0565b6001600160a01b03821660009081526020819052604090205481151560ff9091161515036108b55760405162461bcd60e51b815260206004820181905260248201527f464c41475f414c52454144595f50524553454e545f464f525f4144445245535360448201526064016103f0565b80156108d557600180549060006108cb8361215e565b919050555061093c565b60018054116109265760405162461bcd60e51b815260206004820152601b60248201527f41545f4c454153545f4f4e455f41444d494e5f5245515549524544000000000060448201526064016103f0565b6001805490600061093683612177565b91905055505b6001600160a01b03821660008181526020818152604091829020805460ff191685151590811790915591519182527fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb78910160405180910390a25050565b3360009081526020819052604090205460ff166109c85760405162461bcd60e51b81526004016103f090612063565b86518651811480156109da5750808651145b80156109e65750808551145b80156109f25750808451145b80156109fe5750808351145b8015610a0a5750808251145b610a4e5760405162461bcd60e51b8152602060048201526015602482015274082a4a482b2be988a9c8ea890be9a92a69a82a8869605b1b60448201526064016103f0565b60005b81811015610b2957610b17898281518110610a6e57610a6e61218e565b6020026020010151898381518110610a8857610a8861218e565b6020026020010151898481518110610aa257610aa261218e565b6020026020010151898581518110610abc57610abc61218e565b6020026020010151898681518110610ad657610ad661218e565b6020026020010151898781518110610af057610af061218e565b6020026020010151898881518110610b0a57610b0a61218e565b6020026020010151611348565b80610b218161215e565b915050610a51565b505050505050505050565b6002805403610b555760405162461bcd60e51b81526004016103f0906121a4565b6002805533600081815260046020526040812091610b7390426106fb565b905081600201548111610bbe5760405162461bcd60e51b81526020600482015260136024820152724e4f5448494e475f544f5f574954484452415760681b60448201526064016103f0565b6000826002015482610bd091906120a8565b905060008111610c185760405162461bcd60e51b81526020600482015260136024820152724e4f5448494e475f544f5f574954484452415760681b60448201526064016103f0565b80836002016000828254610c2c91906121db565b925050819055508060036000828254610c4591906120a8565b90915550610c7f90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163383611900565b60405181815233907fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a9060200160405180910390a25050600160025550565b60606005805480602002602001604051908101604052809291908181526020018280548015610d1657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610cf8575b5050505050905090565b6001600160a01b0381166000908152600460209081526040808320815161012081018352815464ffffffffff8082168352600160281b8204811695830195909552600160501b8104851693820193909352600160781b928390048416606082015260018201546080820152600282015460a082018190526003909201546001600160701b03811660c083015260ff600160701b820416151560e08301529290920490921661010082015290610dd584426106fb565b6106f491906120a8565b3360009081526020819052604090205460ff16610e0e5760405162461bcd60e51b81526004016103f090612063565b6002805403610e2f5760405162461bcd60e51b81526004016103f0906121a4565b600280556000610e3d611081565b905081811015610e5f5760405162461bcd60e51b81526004016103f0906121ee565b610e937f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163384611900565b60405182815233907fca1cf43de312865665f595e88f569f9d5246690c07df26e86aba01147e6d13149060200160405180910390a250506001600255565b3360009081526020819052604090205460ff16610f005760405162461bcd60e51b81526004016103f090612063565b6002805403610f215760405162461bcd60e51b81526004016103f0906121a4565b600280556001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811690821603610f905760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b60448201526064016103f0565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610fd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ffb919061221c565b90506000811161101d5760405162461bcd60e51b81526004016103f0906121ee565b6110316001600160a01b0383163383611900565b50506001600255565b3360009081526020819052604090205460ff166110695760405162461bcd60e51b81526004016103f090612063565b61107887878787878787611348565b50505050505050565b6003546040516370a0823160e01b8152306004820152600091906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156110ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611110919061221c565b61111a91906120a8565b905090565b6001600160a01b038116600090815260046020526040812060038101548290600160701b900460ff16611164576003820154600160781b900464ffffffffff16611175565b8154600160281b900464ffffffffff165b60028301546040805161012081018252855464ffffffffff8082168352600160281b820481166020840152600160501b8204811693830193909352600160781b90819004831660608301526001870154608083015260a0820184905260038701546001600160701b03811660c084015260ff600160701b820416151560e0840152049091166101008201529192509061120e9083611220565b61121891906120a8565b949350505050565b60008260e0015115801561123e575061010083015164ffffffffff16155b1561124b575060006107c8565b6000836020015164ffffffffff168364ffffffffff16111561126f57836020015192505b836040015164ffffffffff168364ffffffffff16106112a35760c08401516112a0906001600160701b0316826121db565b90505b836000015164ffffffffff168364ffffffffff1611156106f45783516000906112cc9085612235565b60608601519091506000906112e18184612270565b6112eb9190612294565b90506000866000015187602001516113039190612235565b905060008164ffffffffff168364ffffffffff16896080015161132691906122b5565b61133091906122cc565b905061133c81866121db565b98975050505050505050565b6001600160a01b038716600090815260046020908152604091829020825161012081018452815464ffffffffff808216808452600160281b8304821695840195909552600160501b8204811695830195909552600160781b908190048516606083015260018301546080830152600283015460a08301526003909201546001600160701b03811660c083015260ff600160701b820416151560e083015291909104909216610100830152889190156114395760405162461bcd60e51b81526020600482015260146024820152734752414e545f414c52454144595f45584953545360601b60448201526064016103f0565b6001600160a01b0389166114815760405162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b60448201526064016103f0565b600061148d84866122e0565b6001600160701b0316116114db5760405162461bcd60e51b81526020600482015260156024820152741253959053125117d59154d5115117d05353d55395605a1b60448201526064016103f0565b60008864ffffffffff16116115325760405162461bcd60e51b815260206004820152601760248201527f494e56414c49445f53544152545f54494d455354414d5000000000000000000060448201526064016103f0565b8664ffffffffff168864ffffffffff16106115875760405162461bcd60e51b81526020600482015260156024820152740494e56414c49445f454e445f54494d455354414d5605c1b60448201526064016103f0565b60008564ffffffffff16116115de5760405162461bcd60e51b815260206004820152601860248201527f494e56414c49445f52454c454153455f494e54455256414c000000000000000060448201526064016103f0565b846115e98989612235565b6115f39190612300565b64ffffffffff16156116475760405162461bcd60e51b815260206004820152601760248201527f494e56414c49445f494e54455256414c5f4c454e47544800000000000000000060448201526064016103f0565b60008664ffffffffff1611801561166757506000836001600160701b0316115b801561168157508764ffffffffff168664ffffffffff1611155b806116a3575064ffffffffff86161580156116a357506001600160701b038316155b6116df5760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22fa1a624a32360991b60448201526064016103f0565b6001600160a01b0389166000908152600460205260408120805464ffffffffff8b811669ffffffffffffffffffff1990921691909117600160281b8b8316021769ffffffffffffffffffff60501b1916600160501b8a83160264ffffffffff60781b191617600160781b918916919091021781556001600160701b038681166001830155600382018054600160701b9288166effffffffffffffffffffffffffffff19909116179190911790559061179786866122e0565b6001600160701b03169050806003546117b091906121db565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611814573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611838919061221c565b10156118565760405162461bcd60e51b81526004016103f0906121ee565b806003600082825461186891906121db565b9091555050600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b038d169081179091556040517f9c09911fbc8a564d33adc545eea6a11fb3df08fed887dd6833d15a4e7a77fac4906118eb908590612324565b60405180910390a25050505050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611952908490611957565b505050565b60006119ac826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611a299092919063ffffffff16565b80519091501561195257808060200190518101906119ca9190612333565b6119525760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016103f0565b60606112188484600085856001600160a01b0385163b611a8b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016103f0565b600080866001600160a01b03168587604051611aa79190612374565b60006040518083038185875af1925050503d8060008114611ae4576040519150601f19603f3d011682016040523d82523d6000602084013e611ae9565b606091505b5091509150611af9828286611b04565b979650505050505050565b60608315611b135750816106f4565b825115611b235782518084602001fd5b8160405162461bcd60e51b81526004016103f09190612390565b6001600160a01b0381168114611b5257600080fd5b50565b600060208284031215611b6757600080fd5b81356106f481611b3d565b803564ffffffffff81168114611b8757600080fd5b919050565b60008060408385031215611b9f57600080fd5b8235611baa81611b3d565b9150611bb860208401611b72565b90509250929050565b8015158114611b5257600080fd5b60008060408385031215611be257600080fd5b8235611bed81611b3d565b91506020830135611bfd81611bc1565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611c4757611c47611c08565b604052919050565b600067ffffffffffffffff821115611c6957611c69611c08565b5060051b60200190565b600082601f830112611c8457600080fd5b81356020611c99611c9483611c4f565b611c1e565b82815260059290921b84018101918181019086841115611cb857600080fd5b8286015b84811015611cdc578035611ccf81611b3d565b8352918301918301611cbc565b509695505050505050565b600082601f830112611cf857600080fd5b81356020611d08611c9483611c4f565b82815260059290921b84018101918181019086841115611d2757600080fd5b8286015b84811015611cdc57611d3c81611b72565b8352918301918301611d2b565b80356001600160701b0381168114611b8757600080fd5b600082601f830112611d7157600080fd5b81356020611d81611c9483611c4f565b82815260059290921b84018101918181019086841115611da057600080fd5b8286015b84811015611cdc57611db581611d49565b8352918301918301611da4565b600080600080600080600060e0888a031215611ddd57600080fd5b873567ffffffffffffffff80821115611df557600080fd5b611e018b838c01611c73565b985060208a0135915080821115611e1757600080fd5b611e238b838c01611ce7565b975060408a0135915080821115611e3957600080fd5b611e458b838c01611ce7565b965060608a0135915080821115611e5b57600080fd5b611e678b838c01611ce7565b955060808a0135915080821115611e7d57600080fd5b611e898b838c01611ce7565b945060a08a0135915080821115611e9f57600080fd5b611eab8b838c01611d60565b935060c08a0135915080821115611ec157600080fd5b50611ece8a828b01611d60565b91505092959891949750929550565b6020808252825182820181905260009190848201906040850190845b81811015611f1e5783516001600160a01b031683529284019291840191600101611ef9565b50909695505050505050565b815164ffffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151608083015260a083015160a083015260c0830151611f9160c08401826001600160701b03169052565b5060e0830151611fa560e084018215159052565b506101008381015164ffffffffff8116848301525b505092915050565b600060208284031215611fd457600080fd5b5035919050565b600080600080600080600060e0888a031215611ff657600080fd5b873561200181611b3d565b965061200f60208901611b72565b955061201d60408901611b72565b945061202b60608901611b72565b935061203960808901611b72565b925061204760a08901611d49565b915061205560c08901611d49565b905092959891949750929550565b602080825260159082015274105113525397d050d0d154d4d7d491545552549151605a1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b818103818111156107c8576107c8612092565b805464ffffffffff8082168452602882901c81166020850152605082901c81166040850152607882901c8116606085015260018301546080850152600283015460a085015260038301546001600160701b03811660c0860152915061212a60e0850160ff8460701c1615159052565b6121436101008501828460781c1664ffffffffff169052565b50505050565b82815261014081016106f460208301846120bb565b60006001820161217057612170612092565b5060010190565b60008161218657612186612092565b506000190190565b634e487b7160e01b600052603260045260246000fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b808201808211156107c8576107c8612092565b602080825260149082015273494e53554646494349454e545f42414c414e434560601b604082015260600190565b60006020828403121561222e57600080fd5b5051919050565b64ffffffffff82811682821603908082111561225357612253612092565b5092915050565b634e487b7160e01b600052601260045260246000fd5b600064ffffffffff808416806122885761228861225a565b92169190910492915050565b64ffffffffff818116838216028082169190828114611fba57611fba612092565b80820281158282048414176107c8576107c8612092565b6000826122db576122db61225a565b500490565b6001600160701b0381811683821601908082111561225357612253612092565b600064ffffffffff808416806123185761231861225a565b92169190910692915050565b61012081016107c882846120bb565b60006020828403121561234557600080fd5b81516106f481611bc1565b60005b8381101561236b578181015183820152602001612353565b50506000910152565b60008251612386818460208701612350565b9190910192915050565b60208152600082518060208401526123af816040850160208701612350565b601f01601f1916919091016040019291505056fea26469706673582212202831a0f873829ac6c79c2e01bdd28ce562f47d1e74227aeb0000e27f7c996c4a64736f6c634300081200330000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101215760003560e01c80634e71d92d116100ad578063d77836ce11610071578063d77836ce1461036d578063e50373f914610380578063e7b1d1a414610393578063e865fbc7146103a6578063f37d94b4146103ae57600080fd5b80634e71d92d146101f05780637197dec3146101f8578063898850491461020d5780639d76ea5814610220578063bf31dbf41461025f57600080fd5b806324d7806c116100f457806324d7806c146101725780632b7832b3146101ae5780634a0e701e146101b75780634b0bddd2146101ca5780634b2429d7146101dd57600080fd5b8063137c68fa146101265780631817c5a7146101425780631e2f64a614610157578063241c5b1f1461016a575b600080fd5b61012f60035481565b6040519081526020015b60405180910390f35b610155610150366004611b55565b6103c1565b005b61012f610165366004611b55565b610641565b60055461012f565b61019e610180366004611b55565b6001600160a01b031660009081526020819052604090205460ff1690565b6040519015158152602001610139565b61012f60015481565b61012f6101c5366004611b8c565b6106fb565b6101556101d8366004611bcf565b6107ce565b6101556101eb366004611dc2565b610999565b610155610b34565b610200610cbe565b6040516101399190611edd565b61012f61021b366004611b55565b610d20565b6102477f0000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a81565b6040516001600160a01b039091168152602001610139565b61036061026d366004611b55565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810191909152506001600160a01b0316600090815260046020908152604091829020825161012081018452815464ffffffffff8082168352600160281b8204811694830194909452600160501b8104841694820194909452600160781b938490048316606082015260018201546080820152600282015460a08201526003909101546001600160701b03811660c083015260ff600160701b820416151560e0830152929092041661010082015290565b6040516101399190611f2a565b61015561037b366004611fc2565b610ddf565b61015561038e366004611b55565b610ed1565b6101556103a1366004611fdb565b61103a565b61012f611081565b61012f6103bc366004611b55565b61111f565b3360009081526020819052604090205460ff166103f95760405162461bcd60e51b81526004016103f090612063565b60405180910390fd5b6001600160a01b038116600090815260046020908152604091829020825161012081018452815464ffffffffff808216808452600160281b8304821695840195909552600160501b8204811695830195909552600160781b908190048516606083015260018301546080830152600283015460a08301526003909201546001600160701b03811660c083015260ff600160701b820416151560e0830152919091049092166101008301528291906104e45760405162461bcd60e51b815260206004820152600f60248201526e1393d7d050d512559157d1d4905395608a1b60448201526064016103f0565b8060e001516105275760405162461bcd60e51b815260206004820152600f60248201526e1393d7d050d512559157d1d4905395608a1b60448201526064016103f0565b6001600160a01b03831660009081526004602052604081209061054985610641565b9050808260020154106105935760405162461bcd60e51b81526020600482015260126024820152711393d7d553959154d5115117d05353d5539560721b60448201526064016103f0565b60038201805464ffffffffff42908116600160781b0265ffffffffffff60701b19909216919091179091556000906105cc9087906106fb565b905060006105da82846120a8565b905080600360008282546105ee91906120a8565b92505081905550866001600160a01b03167f0995a2a9d2c36811626ef62e0415f97a104f1c7e7285637e86a20df2f3a4aa598286604051610630929190612149565b60405180910390a250505050505050565b6001600160a01b0381166000908152600460209081526040808320815161012081018352815464ffffffffff8082168352600160281b82048116958301869052600160501b8204811694830194909452600160781b908190048416606083015260018301546080830152600283015460a08301526003909201546001600160701b03811660c083015260ff600160701b820416151560e083015291909104909116610100820152906106f4908290611220565b9392505050565b6001600160a01b0382166000908152600460209081526040808320815161012081018352815464ffffffffff8082168352600160281b8204811695830195909552600160501b8104851693820193909352600160781b928390048416606082015260018201546080820152600282015460a08201526003909101546001600160701b03811660c083015260ff600160701b820416151560e0830181905292900490921661010083015282906107b5578161010001516107b7565b835b90506107c38282611220565b925050505b92915050565b3360009081526020819052604090205460ff166107fd5760405162461bcd60e51b81526004016103f090612063565b6001600160a01b0382166108455760405162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b60448201526064016103f0565b6001600160a01b03821660009081526020819052604090205481151560ff9091161515036108b55760405162461bcd60e51b815260206004820181905260248201527f464c41475f414c52454144595f50524553454e545f464f525f4144445245535360448201526064016103f0565b80156108d557600180549060006108cb8361215e565b919050555061093c565b60018054116109265760405162461bcd60e51b815260206004820152601b60248201527f41545f4c454153545f4f4e455f41444d494e5f5245515549524544000000000060448201526064016103f0565b6001805490600061093683612177565b91905055505b6001600160a01b03821660008181526020818152604091829020805460ff191685151590811790915591519182527fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb78910160405180910390a25050565b3360009081526020819052604090205460ff166109c85760405162461bcd60e51b81526004016103f090612063565b86518651811480156109da5750808651145b80156109e65750808551145b80156109f25750808451145b80156109fe5750808351145b8015610a0a5750808251145b610a4e5760405162461bcd60e51b8152602060048201526015602482015274082a4a482b2be988a9c8ea890be9a92a69a82a8869605b1b60448201526064016103f0565b60005b81811015610b2957610b17898281518110610a6e57610a6e61218e565b6020026020010151898381518110610a8857610a8861218e565b6020026020010151898481518110610aa257610aa261218e565b6020026020010151898581518110610abc57610abc61218e565b6020026020010151898681518110610ad657610ad661218e565b6020026020010151898781518110610af057610af061218e565b6020026020010151898881518110610b0a57610b0a61218e565b6020026020010151611348565b80610b218161215e565b915050610a51565b505050505050505050565b6002805403610b555760405162461bcd60e51b81526004016103f0906121a4565b6002805533600081815260046020526040812091610b7390426106fb565b905081600201548111610bbe5760405162461bcd60e51b81526020600482015260136024820152724e4f5448494e475f544f5f574954484452415760681b60448201526064016103f0565b6000826002015482610bd091906120a8565b905060008111610c185760405162461bcd60e51b81526020600482015260136024820152724e4f5448494e475f544f5f574954484452415760681b60448201526064016103f0565b80836002016000828254610c2c91906121db565b925050819055508060036000828254610c4591906120a8565b90915550610c7f90507f0000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a6001600160a01b03163383611900565b60405181815233907fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a9060200160405180910390a25050600160025550565b60606005805480602002602001604051908101604052809291908181526020018280548015610d1657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610cf8575b5050505050905090565b6001600160a01b0381166000908152600460209081526040808320815161012081018352815464ffffffffff8082168352600160281b8204811695830195909552600160501b8104851693820193909352600160781b928390048416606082015260018201546080820152600282015460a082018190526003909201546001600160701b03811660c083015260ff600160701b820416151560e08301529290920490921661010082015290610dd584426106fb565b6106f491906120a8565b3360009081526020819052604090205460ff16610e0e5760405162461bcd60e51b81526004016103f090612063565b6002805403610e2f5760405162461bcd60e51b81526004016103f0906121a4565b600280556000610e3d611081565b905081811015610e5f5760405162461bcd60e51b81526004016103f0906121ee565b610e937f0000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a6001600160a01b03163384611900565b60405182815233907fca1cf43de312865665f595e88f569f9d5246690c07df26e86aba01147e6d13149060200160405180910390a250506001600255565b3360009081526020819052604090205460ff16610f005760405162461bcd60e51b81526004016103f090612063565b6002805403610f215760405162461bcd60e51b81526004016103f0906121a4565b600280556001600160a01b037f0000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a811690821603610f905760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b60448201526064016103f0565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610fd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ffb919061221c565b90506000811161101d5760405162461bcd60e51b81526004016103f0906121ee565b6110316001600160a01b0383163383611900565b50506001600255565b3360009081526020819052604090205460ff166110695760405162461bcd60e51b81526004016103f090612063565b61107887878787878787611348565b50505050505050565b6003546040516370a0823160e01b8152306004820152600091906001600160a01b037f0000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a16906370a0823190602401602060405180830381865afa1580156110ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611110919061221c565b61111a91906120a8565b905090565b6001600160a01b038116600090815260046020526040812060038101548290600160701b900460ff16611164576003820154600160781b900464ffffffffff16611175565b8154600160281b900464ffffffffff165b60028301546040805161012081018252855464ffffffffff8082168352600160281b820481166020840152600160501b8204811693830193909352600160781b90819004831660608301526001870154608083015260a0820184905260038701546001600160701b03811660c084015260ff600160701b820416151560e0840152049091166101008201529192509061120e9083611220565b61121891906120a8565b949350505050565b60008260e0015115801561123e575061010083015164ffffffffff16155b1561124b575060006107c8565b6000836020015164ffffffffff168364ffffffffff16111561126f57836020015192505b836040015164ffffffffff168364ffffffffff16106112a35760c08401516112a0906001600160701b0316826121db565b90505b836000015164ffffffffff168364ffffffffff1611156106f45783516000906112cc9085612235565b60608601519091506000906112e18184612270565b6112eb9190612294565b90506000866000015187602001516113039190612235565b905060008164ffffffffff168364ffffffffff16896080015161132691906122b5565b61133091906122cc565b905061133c81866121db565b98975050505050505050565b6001600160a01b038716600090815260046020908152604091829020825161012081018452815464ffffffffff808216808452600160281b8304821695840195909552600160501b8204811695830195909552600160781b908190048516606083015260018301546080830152600283015460a08301526003909201546001600160701b03811660c083015260ff600160701b820416151560e083015291909104909216610100830152889190156114395760405162461bcd60e51b81526020600482015260146024820152734752414e545f414c52454144595f45584953545360601b60448201526064016103f0565b6001600160a01b0389166114815760405162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4144445245535360881b60448201526064016103f0565b600061148d84866122e0565b6001600160701b0316116114db5760405162461bcd60e51b81526020600482015260156024820152741253959053125117d59154d5115117d05353d55395605a1b60448201526064016103f0565b60008864ffffffffff16116115325760405162461bcd60e51b815260206004820152601760248201527f494e56414c49445f53544152545f54494d455354414d5000000000000000000060448201526064016103f0565b8664ffffffffff168864ffffffffff16106115875760405162461bcd60e51b81526020600482015260156024820152740494e56414c49445f454e445f54494d455354414d5605c1b60448201526064016103f0565b60008564ffffffffff16116115de5760405162461bcd60e51b815260206004820152601860248201527f494e56414c49445f52454c454153455f494e54455256414c000000000000000060448201526064016103f0565b846115e98989612235565b6115f39190612300565b64ffffffffff16156116475760405162461bcd60e51b815260206004820152601760248201527f494e56414c49445f494e54455256414c5f4c454e47544800000000000000000060448201526064016103f0565b60008664ffffffffff1611801561166757506000836001600160701b0316115b801561168157508764ffffffffff168664ffffffffff1611155b806116a3575064ffffffffff86161580156116a357506001600160701b038316155b6116df5760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22fa1a624a32360991b60448201526064016103f0565b6001600160a01b0389166000908152600460205260408120805464ffffffffff8b811669ffffffffffffffffffff1990921691909117600160281b8b8316021769ffffffffffffffffffff60501b1916600160501b8a83160264ffffffffff60781b191617600160781b918916919091021781556001600160701b038681166001830155600382018054600160701b9288166effffffffffffffffffffffffffffff19909116179190911790559061179786866122e0565b6001600160701b03169050806003546117b091906121db565b6040516370a0823160e01b81523060048201527f0000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a6001600160a01b0316906370a0823190602401602060405180830381865afa158015611814573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611838919061221c565b10156118565760405162461bcd60e51b81526004016103f0906121ee565b806003600082825461186891906121db565b9091555050600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b038d169081179091556040517f9c09911fbc8a564d33adc545eea6a11fb3df08fed887dd6833d15a4e7a77fac4906118eb908590612324565b60405180910390a25050505050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611952908490611957565b505050565b60006119ac826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611a299092919063ffffffff16565b80519091501561195257808060200190518101906119ca9190612333565b6119525760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016103f0565b60606112188484600085856001600160a01b0385163b611a8b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016103f0565b600080866001600160a01b03168587604051611aa79190612374565b60006040518083038185875af1925050503d8060008114611ae4576040519150601f19603f3d011682016040523d82523d6000602084013e611ae9565b606091505b5091509150611af9828286611b04565b979650505050505050565b60608315611b135750816106f4565b825115611b235782518084602001fd5b8160405162461bcd60e51b81526004016103f09190612390565b6001600160a01b0381168114611b5257600080fd5b50565b600060208284031215611b6757600080fd5b81356106f481611b3d565b803564ffffffffff81168114611b8757600080fd5b919050565b60008060408385031215611b9f57600080fd5b8235611baa81611b3d565b9150611bb860208401611b72565b90509250929050565b8015158114611b5257600080fd5b60008060408385031215611be257600080fd5b8235611bed81611b3d565b91506020830135611bfd81611bc1565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611c4757611c47611c08565b604052919050565b600067ffffffffffffffff821115611c6957611c69611c08565b5060051b60200190565b600082601f830112611c8457600080fd5b81356020611c99611c9483611c4f565b611c1e565b82815260059290921b84018101918181019086841115611cb857600080fd5b8286015b84811015611cdc578035611ccf81611b3d565b8352918301918301611cbc565b509695505050505050565b600082601f830112611cf857600080fd5b81356020611d08611c9483611c4f565b82815260059290921b84018101918181019086841115611d2757600080fd5b8286015b84811015611cdc57611d3c81611b72565b8352918301918301611d2b565b80356001600160701b0381168114611b8757600080fd5b600082601f830112611d7157600080fd5b81356020611d81611c9483611c4f565b82815260059290921b84018101918181019086841115611da057600080fd5b8286015b84811015611cdc57611db581611d49565b8352918301918301611da4565b600080600080600080600060e0888a031215611ddd57600080fd5b873567ffffffffffffffff80821115611df557600080fd5b611e018b838c01611c73565b985060208a0135915080821115611e1757600080fd5b611e238b838c01611ce7565b975060408a0135915080821115611e3957600080fd5b611e458b838c01611ce7565b965060608a0135915080821115611e5b57600080fd5b611e678b838c01611ce7565b955060808a0135915080821115611e7d57600080fd5b611e898b838c01611ce7565b945060a08a0135915080821115611e9f57600080fd5b611eab8b838c01611d60565b935060c08a0135915080821115611ec157600080fd5b50611ece8a828b01611d60565b91505092959891949750929550565b6020808252825182820181905260009190848201906040850190845b81811015611f1e5783516001600160a01b031683529284019291840191600101611ef9565b50909695505050505050565b815164ffffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151608083015260a083015160a083015260c0830151611f9160c08401826001600160701b03169052565b5060e0830151611fa560e084018215159052565b506101008381015164ffffffffff8116848301525b505092915050565b600060208284031215611fd457600080fd5b5035919050565b600080600080600080600060e0888a031215611ff657600080fd5b873561200181611b3d565b965061200f60208901611b72565b955061201d60408901611b72565b945061202b60608901611b72565b935061203960808901611b72565b925061204760a08901611d49565b915061205560c08901611d49565b905092959891949750929550565b602080825260159082015274105113525397d050d0d154d4d7d491545552549151605a1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b818103818111156107c8576107c8612092565b805464ffffffffff8082168452602882901c81166020850152605082901c81166040850152607882901c8116606085015260018301546080850152600283015460a085015260038301546001600160701b03811660c0860152915061212a60e0850160ff8460701c1615159052565b6121436101008501828460781c1664ffffffffff169052565b50505050565b82815261014081016106f460208301846120bb565b60006001820161217057612170612092565b5060010190565b60008161218657612186612092565b506000190190565b634e487b7160e01b600052603260045260246000fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b808201808211156107c8576107c8612092565b602080825260149082015273494e53554646494349454e545f42414c414e434560601b604082015260600190565b60006020828403121561222e57600080fd5b5051919050565b64ffffffffff82811682821603908082111561225357612253612092565b5092915050565b634e487b7160e01b600052601260045260246000fd5b600064ffffffffff808416806122885761228861225a565b92169190910492915050565b64ffffffffff818116838216028082169190828114611fba57611fba612092565b80820281158282048414176107c8576107c8612092565b6000826122db576122db61225a565b500490565b6001600160701b0381811683821601908082111561225357612253612092565b600064ffffffffff808416806123185761231861225a565b92169190910692915050565b61012081016107c882846120bb565b60006020828403121561234557600080fd5b81516106f481611bc1565b60005b8381101561236b578181015183820152602001612353565b50506000910152565b60008251612386818460208701612350565b9190910192915050565b60208152600082518060208401526123af816040850160208701612350565b601f01601f1916919091016040019291505056fea26469706673582212202831a0f873829ac6c79c2e01bdd28ce562f47d1e74227aeb0000e27f7c996c4a64736f6c63430008120033