false
false

Contract Address Details

0xa7b6d93bf23d524eb685b63ff5bb4f7248a57a5e

Contract Name
Subscriptions
Creator
0x3455fa–f3666d at 0xa96728–093df2
Balance
0 ETH ( )
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
67046695
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
Subscriptions




Optimization enabled
true
Compiler version
v0.8.12+commit.f00d7308




Optimization runs
10
Verified at
2023-08-24T23:26:33.523000Z

contracts/Subscriptions.sol

/*
    SPDX-License-Identifier: Apache-2.0

    Copyright 2021 Reddit, Inc

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

pragma solidity ^0.8.9;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "./ISubredditPoints.sol";
import "./opengsn/ERC2771Recipient.sol";

/**
 * ERRORS
 */
/// @notice If address = address(0)
error Subscriptions__AddressIsZero();
/// @notice Subscription cancellation method can only be called by payer or recipient
error Subscriptions__SubscriptionCanOnlyBeCancelledByPayerOrRecipient();
/// @notice can only renew if subscription expired
error Subscriptions__ToEarlyToRenew();
/// @notice Subscription is cancelled since there is no payer
error Subscriptions__SubscriptionIsCancelled();
/// @notice If a uint parameter passed in 0
error Subscriptions__ParameterShouldBeGreaterThanZero();
/// @notice if recipients supplied doesn't match the array specifying if it is for subscribing or renewal
error Subscriptions__RecipientsAndIsRenewOperationArrayMustBeSameLength(uint256 lenRecipients, uint256 lenIsRenew);
/// @notice updatePrice() caller is unauthorized
error Subscriptions__UnauthorizedUpdatePrice();

/**
 * @title Subscriptions
 * @notice Subscriptions tracks subreddit membership subscriptions
 */
contract Subscriptions is Initializable, OwnableUpgradeable, ERC2771Recipient {
    using AddressUpgradeable for address;

    event Subscribed(
        address indexed recipient,
        address indexed payer,
        uint256 burnedPoints,
        uint256 expiresAt,
        bool renewable);
    event Canceled(address indexed recipient, uint256 expiresAt);
    event DurationUpdated(uint256 duration);
    event PriceUpdated(uint256 price);
    event RenewBeforeUpdated(uint256 renewBefore);
    event TrustedForwarderUpdated(address newForwarder);
    event KarmaSourceUpdated(address current, address old);

    // ------------------------------------------------------------------------------------
    // VARIABLES BLOCK, MAKE SURE ONLY ADD TO THE END

    uint256 internal _renewBefore;
    uint256 internal _duration;
    uint256 internal _price;

    // maps address to expiration time
    mapping(address => uint256) internal _subscriptions;
    // maps address of recipient to address of payer
    mapping(address => address) internal _payers;
    ISubredditPoints public subredditPoints;

    string private __unused_slot_placeholder_do_not_remove; // removed slot for versionRecipient, replaced by function 

    address public prevKarmaSource;
    address public karmaSource;

    // END OF VARS
    // ------------------------------------------------------------------------------------

    function initialize(
        address owner_,
        address subredditPoints_,
        address trustedForwarder_,
        address karmaSource_,
        uint256 price_,
        uint256 duration_,
        uint256 renewBefore_
    ) external initializer {
        if (owner_ == address(0) || subredditPoints_ == address(0))
            revert Subscriptions__AddressIsZero();
        OwnableUpgradeable.__Ownable_init();
        if (owner_ != _msgSender()) {
            OwnableUpgradeable.transferOwnership(owner_);
        }
        subredditPoints = ISubredditPoints(subredditPoints_);
        _setTrustedForwarder(trustedForwarder_);
        _updatePrice(price_);
        _updateDuration(duration_);
        _updateRenewBefore(renewBefore_);
        // call twice to ensure both karmaSource & prevKarmaSource are set to karmaSource_
        _updateKarmaSource(karmaSource_);
        _updateKarmaSource(karmaSource_);
    }

    function cancel(address recipient) external virtual {
        address payer = _payers[recipient];
        if (_msgSender() != payer && _msgSender() != recipient)
            revert Subscriptions__SubscriptionCanOnlyBeCancelledByPayerOrRecipient();
        delete _payers[recipient];
        emit Canceled(recipient, _subscriptions[recipient]);
    }

    function renew(address recipient) external virtual {
        address payer = _payers[recipient];
        if (payer == address(0))
            revert Subscriptions__SubscriptionIsCancelled();
        // solium-disable-next-line security/no-block-members
        if (expiration(recipient) >= block.timestamp + _renewBefore)
            revert Subscriptions__ToEarlyToRenew();
        _subscribe(payer, recipient, true);
    }

    function subscribe(address recipient, bool renewable) external virtual  {
        address payer = _msgSender();
        if (renewable) {
            _payers[recipient] = payer;
        }
        _subscribe(payer, recipient, renewable);
    }

    /**
     * @notice subscribe or renew for supplied recipients at once to save gas.
     * @dev subscribe in batches assumes renewable to be true.
     * Since this is used for batches, it can't revert (as reverting would undo the whole batch).
     * Emits Subscribed event with the difference that `renewable` is true to renew operation.
     * @param recipients - array of recipient addresses to subscribe or renew
     * @param isRenewOperation -
     */
    function subscribeAndRenewInBatches(address[] calldata recipients, bool[] calldata isRenewOperation)
    external virtual {
        if (recipients.length != isRenewOperation.length)
            revert Subscriptions__RecipientsAndIsRenewOperationArrayMustBeSameLength(
                recipients.length, isRenewOperation.length);
        for (uint i=0; i<recipients.length; i++) {
            bool toRenew = isRenewOperation[i];
            address recipient = recipients[i];
            if (recipient == address(0))
                continue;
            address payer;
            if (toRenew) {
                payer = _payers[recipient];
                // Subscription is cancelled - skip.
                if (payer == address(0))
                    continue;
                // If subscription to early to renew - skip it.
                // solium-disable-next-line security/no-block-members
                if (expiration(recipient) >= block.timestamp + _renewBefore)
                    continue;
            } else {
                payer = _msgSender();
            }

            // Use .call() so that no reverts happen (as it would impact the whole batch).
            (bool success, ) = address(subredditPoints).call(
                abi.encodeWithSignature("operatorBurn(address,uint256,bytes,bytes)", payer, _price, "", "")
            );
            if (success) {
                // Set new expiration for subscription only if burn succeeds.
                uint256 newExpiration = _updateNewSubscriptionExpiryTime(recipient);
                emit Subscribed(recipient, payer, _price, newExpiration, toRenew);
            }
        }
    }

    function _subscribe(address payer, address recipient, bool renewable) internal {
        if (recipient == address(0))
            revert Subscriptions__AddressIsZero();
        uint256 newExpiration = _updateNewSubscriptionExpiryTime(recipient);
        emit Subscribed(recipient, payer, _price, newExpiration, renewable);
        subredditPoints.operatorBurn(payer, _price, "", "");
    }

    function _updateNewSubscriptionExpiryTime(address recipient) internal returns (uint256 newExpiration)  {
        uint256 expirationAt = _subscriptions[recipient];
        // solium-disable-next-line security/no-block-members
        if (expirationAt < block.timestamp) {
            // solium-disable-next-line security/no-block-members
            expirationAt = block.timestamp;
        }
        newExpiration = expirationAt + _duration;
        _subscriptions[recipient] = newExpiration;
    }

    function updateDuration(uint256 duration_) external onlyOwner {
        _updateDuration(duration_);
    }

    function _updateDuration(uint256 duration_) internal {
        if (duration_ == 0)
            revert Subscriptions__ParameterShouldBeGreaterThanZero();
        _duration = duration_;
        emit DurationUpdated(duration_);
    }

    function updatePrice(uint256 price_) external {
        if (_msgSender() != karmaSource && _msgSender() != prevKarmaSource && _msgSender() != owner())
            revert Subscriptions__UnauthorizedUpdatePrice();
        _updatePrice(price_);
    }

    function _updatePrice(uint256 price_) internal {
        if (price_ == 0)
            revert Subscriptions__ParameterShouldBeGreaterThanZero();
        _price = price_;
        emit PriceUpdated(price_);
    }

    function updateRenewBefore(uint256 renewBefore_) external onlyOwner {
        _updateRenewBefore(renewBefore_);
    }

    function _updateRenewBefore(uint256 renewBefore_) internal {
        if (renewBefore_ == 0)
            revert Subscriptions__ParameterShouldBeGreaterThanZero();
        _renewBefore = renewBefore_;
        emit RenewBeforeUpdated(renewBefore_);
    }

    function updateTrustedForwarder(address forwarder_) external onlyOwner {
        _setTrustedForwarder(forwarder_);
        emit TrustedForwarderUpdated(forwarder_);
    }

    function _updateKarmaSource(address karmaSource_) internal {
        prevKarmaSource = karmaSource;
        karmaSource = karmaSource_;
        emit KarmaSourceUpdated(karmaSource, prevKarmaSource);
    }

    function updateKarmaSource(address karmaSource_) external onlyOwner {
        _updateKarmaSource(karmaSource_);
    }

    function duration() external view returns (uint256) {
        return _duration;
    }

    function price() external view returns (uint256) {
        return _price;
    }

    function renewBefore() external view returns (uint256) {
        return _renewBefore;
    }

    function expiration(address account) public view returns (uint256) {
        return _subscriptions[account];
    }

    function payerOf(address account) public view returns (address) {
        return _payers[account];
    }

    function _msgSender() internal view override(ERC2771Recipient, ContextUpgradeable) returns (address ret) {
        ret = ERC2771Recipient._msgSender();
    }

    function _msgData() internal view override(ERC2771Recipient, ContextUpgradeable) returns (bytes calldata ret) {
        ret = ERC2771Recipient._msgData();
    }

    // required by ERC2771Recipient
    function versionRecipient() external pure override returns (string memory) {
        return "0+Subscriptions";
    }
}
        

@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.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 IERC20Upgradeable {
    /**
     * @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);
}
          

Contract ABI

[{"type":"error","name":"Subscriptions__AddressIsZero","inputs":[]},{"type":"error","name":"Subscriptions__ParameterShouldBeGreaterThanZero","inputs":[]},{"type":"error","name":"Subscriptions__RecipientsAndIsRenewOperationArrayMustBeSameLength","inputs":[{"type":"uint256","name":"lenRecipients","internalType":"uint256"},{"type":"uint256","name":"lenIsRenew","internalType":"uint256"}]},{"type":"error","name":"Subscriptions__SubscriptionCanOnlyBeCancelledByPayerOrRecipient","inputs":[]},{"type":"error","name":"Subscriptions__SubscriptionIsCancelled","inputs":[]},{"type":"error","name":"Subscriptions__ToEarlyToRenew","inputs":[]},{"type":"error","name":"Subscriptions__UnauthorizedUpdatePrice","inputs":[]},{"type":"event","name":"Canceled","inputs":[{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"uint256","name":"expiresAt","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DurationUpdated","inputs":[{"type":"uint256","name":"duration","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"KarmaSourceUpdated","inputs":[{"type":"address","name":"current","internalType":"address","indexed":false},{"type":"address","name":"old","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"PriceUpdated","inputs":[{"type":"uint256","name":"price","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RenewBeforeUpdated","inputs":[{"type":"uint256","name":"renewBefore","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Subscribed","inputs":[{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"address","name":"payer","internalType":"address","indexed":true},{"type":"uint256","name":"burnedPoints","internalType":"uint256","indexed":false},{"type":"uint256","name":"expiresAt","internalType":"uint256","indexed":false},{"type":"bool","name":"renewable","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"TrustedForwarderUpdated","inputs":[{"type":"address","name":"newForwarder","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancel","inputs":[{"type":"address","name":"recipient","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"duration","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"expiration","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"forwarder","internalType":"address"}],"name":"getTrustedForwarder","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"owner_","internalType":"address"},{"type":"address","name":"subredditPoints_","internalType":"address"},{"type":"address","name":"trustedForwarder_","internalType":"address"},{"type":"address","name":"karmaSource_","internalType":"address"},{"type":"uint256","name":"price_","internalType":"uint256"},{"type":"uint256","name":"duration_","internalType":"uint256"},{"type":"uint256","name":"renewBefore_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isTrustedForwarder","inputs":[{"type":"address","name":"forwarder","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"karmaSource","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"payerOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"prevKarmaSource","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"price","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renew","inputs":[{"type":"address","name":"recipient","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"renewBefore","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ISubredditPoints"}],"name":"subredditPoints","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"subscribe","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"bool","name":"renewable","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"subscribeAndRenewInBatches","inputs":[{"type":"address[]","name":"recipients","internalType":"address[]"},{"type":"bool[]","name":"isRenewOperation","internalType":"bool[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateDuration","inputs":[{"type":"uint256","name":"duration_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateKarmaSource","inputs":[{"type":"address","name":"karmaSource_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updatePrice","inputs":[{"type":"uint256","name":"price_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateRenewBefore","inputs":[{"type":"uint256","name":"renewBefore_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateTrustedForwarder","inputs":[{"type":"address","name":"forwarder_","internalType":"address"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"versionRecipient","inputs":[]}]
              

Contract Creation Code

0x608060405234801561001057600080fd5b50611325806100206000396000f3fe608060405234801561001057600080fd5b50600436106101275760003560e01c806215d5b11461012c57806304dbe3fe146101555780630fb5a6b4146101765780631085c9ba1461017e57806319bc469e146101865780631b50ad091461019b578063474738b7146101ae578063486ff0cd146101c15780634c33fe94146101f2578063572b6c05146102055780635a5f219414610228578063715018a61461023b57806374a4659b146102435780637f3619281461026f578063881d54e01461028257806389304061146102955780638d6cc56d146102a85780638da5cb5b146102bb578063a035b1fe146102c3578063ce1b815f146102cb578063e77fc7a4146102dc578063f2fde38b146102ef578063f90b031114610302578063fdc28c8c14610315575b600080fd5b60a05461013f906001600160a01b031681565b60405161014c9190610f35565b60405180910390f35b610168610163366004610f65565b610328565b60405190815260200161014c565b609954610168565b609854610168565b610199610194366004610f97565b610343565b005b6101996101a9366004610fca565b610394565b6101996101bc366004610fca565b6103e8565b604080518082018252600f81526e302b537562736372697074696f6e7360881b6020820152905161014c9190611013565b610199610200366004610f65565b610430565b610218610213366004610f65565b610508565b604051901515815260200161014c565b609f5461013f906001600160a01b031681565b61019961051c565b61013f610251366004610f65565b6001600160a01b039081166000908152609c60205260409020541690565b61019961027d366004610f65565b610567565b610199610290366004611091565b6105af565b609d5461013f906001600160a01b031681565b6101996102b6366004610fca565b6107c5565b61013f61085e565b609a54610168565b6065546001600160a01b031661013f565b6101996102ea3660046110fc565b61086d565b6101996102fd366004610f65565b6109ea565b610199610310366004610f65565b610a97565b610199610323366004610f65565b610b19565b6001600160a01b03166000908152609b602052604090205490565b600061034d610b96565b90508115610384576001600160a01b038381166000908152609c6020526040902080546001600160a01b0319169183169190911790555b61038f818484610ba5565b505050565b61039c610b96565b6001600160a01b03166103ad61085e565b6001600160a01b0316146103dc5760405162461bcd60e51b81526004016103d39061116b565b60405180910390fd5b6103e581610c7a565b50565b6103f0610b96565b6001600160a01b031661040161085e565b6001600160a01b0316146104275760405162461bcd60e51b81526004016103d39061116b565b6103e581610ccd565b6001600160a01b038082166000908152609c60205260409020541680610454610b96565b6001600160a01b0316141580156104845750816001600160a01b0316610478610b96565b6001600160a01b031614155b156104a257604051630d5b847360e11b815260040160405180910390fd5b6001600160a01b0382166000818152609c6020908152604080832080546001600160a01b0319169055609b8252918290205491519182527ff3a6ef5718c05d9183af076f5753197b68b04552a763c34796637d6134bdd0f2910160405180910390a25050565b6065546001600160a01b0391821691161490565b610524610b96565b6001600160a01b031661053561085e565b6001600160a01b03161461055b5760405162461bcd60e51b81526004016103d39061116b565b6105656000610d20565b565b61056f610b96565b6001600160a01b031661058061085e565b6001600160a01b0316146105a65760405162461bcd60e51b81526004016103d39061116b565b6103e581610d72565b8281146105d9576040516345f3605d60e11b815260048101849052602481018290526044016103d3565b60005b838110156107be5760008383838181106105f8576105f86111a0565b905060200201602081019061060d91906111b6565b90506000868684818110610623576106236111a0565b90506020020160208101906106389190610f65565b90506001600160a01b03811661064f5750506107ac565b600082156106a857506001600160a01b038082166000908152609c60205260409020541680610680575050506107ac565b60985461068d90426111e7565b61069683610328565b106106a3575050506107ac565b6106b3565b6106b0610b96565b90505b609d54609a546040516000926001600160a01b0316916106d8918591906024016111ff565b60408051601f198184030181529181526020820180516001600160e01b031663fc673c4f60e01b1790525161070d9190611235565b6000604051808303816000865af19150503d806000811461074a576040519150601f19603f3d011682016040523d82523d6000602084013e61074f565b606091505b5050905080156107a757600061076484610dd8565b9050826001600160a01b0316846001600160a01b03166000805160206112d0833981519152609a54848960405161079d93929190611251565b60405180910390a3505b505050505b806107b681611269565b9150506105dc565b5050505050565b60a0546001600160a01b03166107d9610b96565b6001600160a01b03161415801561080b5750609f546001600160a01b03166107ff610b96565b6001600160a01b031614155b8015610837575061081a61085e565b6001600160a01b031661082b610b96565b6001600160a01b031614155b156108555760405163106468c560e31b815260040160405180910390fd5b6103e581610e2c565b6033546001600160a01b031690565b600054610100900460ff166108885760005460ff161561088c565b303b155b6108ef5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016103d3565b600054610100900460ff16158015610911576000805461ffff19166101011790555b6001600160a01b038816158061092e57506001600160a01b038716155b1561094c5760405163819bacf360e01b815260040160405180910390fd5b610954610e7f565b61095c610b96565b6001600160a01b0316886001600160a01b03161461097d5761097d886109ea565b609d80546001600160a01b0319166001600160a01b0389161790556109a186610eae565b6109aa84610e2c565b6109b383610c7a565b6109bc82610ccd565b6109c585610d72565b6109ce85610d72565b80156109e0576000805461ff00191690555b5050505050505050565b6109f2610b96565b6001600160a01b0316610a0361085e565b6001600160a01b031614610a295760405162461bcd60e51b81526004016103d39061116b565b6001600160a01b038116610a8e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103d3565b6103e581610d20565b610a9f610b96565b6001600160a01b0316610ab061085e565b6001600160a01b031614610ad65760405162461bcd60e51b81526004016103d39061116b565b610adf81610eae565b7fa4388ecc389b1390354ae0c65a856c0d7dd4fb648419f5d3ac0b99e38f46fd1181604051610b0e9190610f35565b60405180910390a150565b6001600160a01b038082166000908152609c60205260409020541680610b5257604051634f40d80160e11b815260040160405180910390fd5b609854610b5f90426111e7565b610b6883610328565b10610b865760405163b90c3ad160e01b815260040160405180910390fd5b610b9281836001610ba5565b5050565b6000610ba0610ed0565b905090565b6001600160a01b038216610bcc5760405163819bacf360e01b815260040160405180910390fd5b6000610bd783610dd8565b9050836001600160a01b0316836001600160a01b03166000805160206112d0833981519152609a548486604051610c1093929190611251565b60405180910390a3609d54609a5460405163fc673c4f60e01b81526001600160a01b039092169163fc673c4f91610c4c918891906004016111ff565b600060405180830381600087803b158015610c6657600080fd5b505af11580156109e0573d6000803e3d6000fd5b80610c9857604051633f391d0760e11b815260040160405180910390fd5b60998190556040518181527f91abcc2d6823e3a3f11d31b208dd3065d2c6a791f1c7c9fe96a42ce12897eac590602001610b0e565b80610ceb57604051633f391d0760e11b815260040160405180910390fd5b60988190556040518181527f910b02595d6b5ce3e26daa3fa59d878d520b015b80981aaf497e918a841b4e5190602001610b0e565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60a08054609f80546001600160a01b038084166001600160a01b0319928316811790935592169184169182179092556040805191825260208201929092527fea601ecdff79475b71fb4d1545a7ebca8349b2af1ca1ff06dae26d8598067e129101610b0e565b6001600160a01b0381166000908152609b602052604081205442811015610dfc5750425b609954610e0990826111e7565b6001600160a01b039093166000908152609b602052604090208390555090919050565b80610e4a57604051633f391d0760e11b815260040160405180910390fd5b609a8190556040518181527f66cbca4f3c64fecf1dcb9ce094abcf7f68c3450a1d4e3a8e917dd621edb4ebe090602001610b0e565b600054610100900460ff16610ea65760405162461bcd60e51b81526004016103d390611284565b610565610efe565b606580546001600160a01b0319166001600160a01b0392909216919091179055565b600060143610801590610ee75750610ee733610508565b15610ef9575060131936013560601c90565b503390565b600054610100900460ff16610f255760405162461bcd60e51b81526004016103d390611284565b610565610f30610b96565b610d20565b6001600160a01b0391909116815260200190565b80356001600160a01b0381168114610f6057600080fd5b919050565b600060208284031215610f7757600080fd5b610f8082610f49565b9392505050565b80358015158114610f6057600080fd5b60008060408385031215610faa57600080fd5b610fb383610f49565b9150610fc160208401610f87565b90509250929050565b600060208284031215610fdc57600080fd5b5035919050565b60005b83811015610ffe578181015183820152602001610fe6565b8381111561100d576000848401525b50505050565b6020815260008251806020840152611032816040850160208701610fe3565b601f01601f19169190910160400192915050565b60008083601f84011261105857600080fd5b5081356001600160401b0381111561106f57600080fd5b6020830191508360208260051b850101111561108a57600080fd5b9250929050565b600080600080604085870312156110a757600080fd5b84356001600160401b03808211156110be57600080fd5b6110ca88838901611046565b909650945060208701359150808211156110e357600080fd5b506110f087828801611046565b95989497509550505050565b600080600080600080600060e0888a03121561111757600080fd5b61112088610f49565b965061112e60208901610f49565b955061113c60408901610f49565b945061114a60608901610f49565b9699959850939660808101359560a0820135955060c0909101359350915050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156111c857600080fd5b610f8082610f87565b634e487b7160e01b600052601160045260246000fd5b600082198211156111fa576111fa6111d1565b500190565b6001600160a01b039290921682526020820152608060408201819052600090820181905260a06060830181905282015260c00190565b60008251611247818460208701610fe3565b9190910192915050565b92835260208301919091521515604082015260600190565b600060001982141561127d5761127d6111d1565b5060010190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056fed38b19993495aaecd8b724dae5aba66885a681f4cbe90b11feb232ef36ed4e35a2646970667358221220c0120e66eaa567172bf5be903d3203ca65ee2c678badcee05200d5b9691008ef64736f6c634300080c0033

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101275760003560e01c806215d5b11461012c57806304dbe3fe146101555780630fb5a6b4146101765780631085c9ba1461017e57806319bc469e146101865780631b50ad091461019b578063474738b7146101ae578063486ff0cd146101c15780634c33fe94146101f2578063572b6c05146102055780635a5f219414610228578063715018a61461023b57806374a4659b146102435780637f3619281461026f578063881d54e01461028257806389304061146102955780638d6cc56d146102a85780638da5cb5b146102bb578063a035b1fe146102c3578063ce1b815f146102cb578063e77fc7a4146102dc578063f2fde38b146102ef578063f90b031114610302578063fdc28c8c14610315575b600080fd5b60a05461013f906001600160a01b031681565b60405161014c9190610f35565b60405180910390f35b610168610163366004610f65565b610328565b60405190815260200161014c565b609954610168565b609854610168565b610199610194366004610f97565b610343565b005b6101996101a9366004610fca565b610394565b6101996101bc366004610fca565b6103e8565b604080518082018252600f81526e302b537562736372697074696f6e7360881b6020820152905161014c9190611013565b610199610200366004610f65565b610430565b610218610213366004610f65565b610508565b604051901515815260200161014c565b609f5461013f906001600160a01b031681565b61019961051c565b61013f610251366004610f65565b6001600160a01b039081166000908152609c60205260409020541690565b61019961027d366004610f65565b610567565b610199610290366004611091565b6105af565b609d5461013f906001600160a01b031681565b6101996102b6366004610fca565b6107c5565b61013f61085e565b609a54610168565b6065546001600160a01b031661013f565b6101996102ea3660046110fc565b61086d565b6101996102fd366004610f65565b6109ea565b610199610310366004610f65565b610a97565b610199610323366004610f65565b610b19565b6001600160a01b03166000908152609b602052604090205490565b600061034d610b96565b90508115610384576001600160a01b038381166000908152609c6020526040902080546001600160a01b0319169183169190911790555b61038f818484610ba5565b505050565b61039c610b96565b6001600160a01b03166103ad61085e565b6001600160a01b0316146103dc5760405162461bcd60e51b81526004016103d39061116b565b60405180910390fd5b6103e581610c7a565b50565b6103f0610b96565b6001600160a01b031661040161085e565b6001600160a01b0316146104275760405162461bcd60e51b81526004016103d39061116b565b6103e581610ccd565b6001600160a01b038082166000908152609c60205260409020541680610454610b96565b6001600160a01b0316141580156104845750816001600160a01b0316610478610b96565b6001600160a01b031614155b156104a257604051630d5b847360e11b815260040160405180910390fd5b6001600160a01b0382166000818152609c6020908152604080832080546001600160a01b0319169055609b8252918290205491519182527ff3a6ef5718c05d9183af076f5753197b68b04552a763c34796637d6134bdd0f2910160405180910390a25050565b6065546001600160a01b0391821691161490565b610524610b96565b6001600160a01b031661053561085e565b6001600160a01b03161461055b5760405162461bcd60e51b81526004016103d39061116b565b6105656000610d20565b565b61056f610b96565b6001600160a01b031661058061085e565b6001600160a01b0316146105a65760405162461bcd60e51b81526004016103d39061116b565b6103e581610d72565b8281146105d9576040516345f3605d60e11b815260048101849052602481018290526044016103d3565b60005b838110156107be5760008383838181106105f8576105f86111a0565b905060200201602081019061060d91906111b6565b90506000868684818110610623576106236111a0565b90506020020160208101906106389190610f65565b90506001600160a01b03811661064f5750506107ac565b600082156106a857506001600160a01b038082166000908152609c60205260409020541680610680575050506107ac565b60985461068d90426111e7565b61069683610328565b106106a3575050506107ac565b6106b3565b6106b0610b96565b90505b609d54609a546040516000926001600160a01b0316916106d8918591906024016111ff565b60408051601f198184030181529181526020820180516001600160e01b031663fc673c4f60e01b1790525161070d9190611235565b6000604051808303816000865af19150503d806000811461074a576040519150601f19603f3d011682016040523d82523d6000602084013e61074f565b606091505b5050905080156107a757600061076484610dd8565b9050826001600160a01b0316846001600160a01b03166000805160206112d0833981519152609a54848960405161079d93929190611251565b60405180910390a3505b505050505b806107b681611269565b9150506105dc565b5050505050565b60a0546001600160a01b03166107d9610b96565b6001600160a01b03161415801561080b5750609f546001600160a01b03166107ff610b96565b6001600160a01b031614155b8015610837575061081a61085e565b6001600160a01b031661082b610b96565b6001600160a01b031614155b156108555760405163106468c560e31b815260040160405180910390fd5b6103e581610e2c565b6033546001600160a01b031690565b600054610100900460ff166108885760005460ff161561088c565b303b155b6108ef5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016103d3565b600054610100900460ff16158015610911576000805461ffff19166101011790555b6001600160a01b038816158061092e57506001600160a01b038716155b1561094c5760405163819bacf360e01b815260040160405180910390fd5b610954610e7f565b61095c610b96565b6001600160a01b0316886001600160a01b03161461097d5761097d886109ea565b609d80546001600160a01b0319166001600160a01b0389161790556109a186610eae565b6109aa84610e2c565b6109b383610c7a565b6109bc82610ccd565b6109c585610d72565b6109ce85610d72565b80156109e0576000805461ff00191690555b5050505050505050565b6109f2610b96565b6001600160a01b0316610a0361085e565b6001600160a01b031614610a295760405162461bcd60e51b81526004016103d39061116b565b6001600160a01b038116610a8e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103d3565b6103e581610d20565b610a9f610b96565b6001600160a01b0316610ab061085e565b6001600160a01b031614610ad65760405162461bcd60e51b81526004016103d39061116b565b610adf81610eae565b7fa4388ecc389b1390354ae0c65a856c0d7dd4fb648419f5d3ac0b99e38f46fd1181604051610b0e9190610f35565b60405180910390a150565b6001600160a01b038082166000908152609c60205260409020541680610b5257604051634f40d80160e11b815260040160405180910390fd5b609854610b5f90426111e7565b610b6883610328565b10610b865760405163b90c3ad160e01b815260040160405180910390fd5b610b9281836001610ba5565b5050565b6000610ba0610ed0565b905090565b6001600160a01b038216610bcc5760405163819bacf360e01b815260040160405180910390fd5b6000610bd783610dd8565b9050836001600160a01b0316836001600160a01b03166000805160206112d0833981519152609a548486604051610c1093929190611251565b60405180910390a3609d54609a5460405163fc673c4f60e01b81526001600160a01b039092169163fc673c4f91610c4c918891906004016111ff565b600060405180830381600087803b158015610c6657600080fd5b505af11580156109e0573d6000803e3d6000fd5b80610c9857604051633f391d0760e11b815260040160405180910390fd5b60998190556040518181527f91abcc2d6823e3a3f11d31b208dd3065d2c6a791f1c7c9fe96a42ce12897eac590602001610b0e565b80610ceb57604051633f391d0760e11b815260040160405180910390fd5b60988190556040518181527f910b02595d6b5ce3e26daa3fa59d878d520b015b80981aaf497e918a841b4e5190602001610b0e565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60a08054609f80546001600160a01b038084166001600160a01b0319928316811790935592169184169182179092556040805191825260208201929092527fea601ecdff79475b71fb4d1545a7ebca8349b2af1ca1ff06dae26d8598067e129101610b0e565b6001600160a01b0381166000908152609b602052604081205442811015610dfc5750425b609954610e0990826111e7565b6001600160a01b039093166000908152609b602052604090208390555090919050565b80610e4a57604051633f391d0760e11b815260040160405180910390fd5b609a8190556040518181527f66cbca4f3c64fecf1dcb9ce094abcf7f68c3450a1d4e3a8e917dd621edb4ebe090602001610b0e565b600054610100900460ff16610ea65760405162461bcd60e51b81526004016103d390611284565b610565610efe565b606580546001600160a01b0319166001600160a01b0392909216919091179055565b600060143610801590610ee75750610ee733610508565b15610ef9575060131936013560601c90565b503390565b600054610100900460ff16610f255760405162461bcd60e51b81526004016103d390611284565b610565610f30610b96565b610d20565b6001600160a01b0391909116815260200190565b80356001600160a01b0381168114610f6057600080fd5b919050565b600060208284031215610f7757600080fd5b610f8082610f49565b9392505050565b80358015158114610f6057600080fd5b60008060408385031215610faa57600080fd5b610fb383610f49565b9150610fc160208401610f87565b90509250929050565b600060208284031215610fdc57600080fd5b5035919050565b60005b83811015610ffe578181015183820152602001610fe6565b8381111561100d576000848401525b50505050565b6020815260008251806020840152611032816040850160208701610fe3565b601f01601f19169190910160400192915050565b60008083601f84011261105857600080fd5b5081356001600160401b0381111561106f57600080fd5b6020830191508360208260051b850101111561108a57600080fd5b9250929050565b600080600080604085870312156110a757600080fd5b84356001600160401b03808211156110be57600080fd5b6110ca88838901611046565b909650945060208701359150808211156110e357600080fd5b506110f087828801611046565b95989497509550505050565b600080600080600080600060e0888a03121561111757600080fd5b61112088610f49565b965061112e60208901610f49565b955061113c60408901610f49565b945061114a60608901610f49565b9699959850939660808101359560a0820135955060c0909101359350915050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156111c857600080fd5b610f8082610f87565b634e487b7160e01b600052601160045260246000fd5b600082198211156111fa576111fa6111d1565b500190565b6001600160a01b039290921682526020820152608060408201819052600090820181905260a06060830181905282015260c00190565b60008251611247818460208701610fe3565b9190910192915050565b92835260208301919091521515604082015260600190565b600060001982141561127d5761127d6111d1565b5060010190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056fed38b19993495aaecd8b724dae5aba66885a681f4cbe90b11feb232ef36ed4e35a2646970667358221220c0120e66eaa567172bf5be903d3203ca65ee2c678badcee05200d5b9691008ef64736f6c634300080c0033