ETH Price: $3,420.32 (+0.86%)
Gas: 47 Gwei

Contract

0xD249aD8fA4646C303028a8d29cf8568A38897C55
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Initialize161312282022-12-07 7:00:59451 days ago1670396459IN
0xD249aD8f...A38897C55
0 ETH0.003748713.15333899
0x61012060129511462021-08-03 8:05:55942 days ago1627977955IN
 Create: SafetyModuleV1
0 ETH0.192563742

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SafetyModuleV1

Compiler Version
v0.7.5+commit.eb77ed08

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 13 of 38 : SafetyModuleV1.sol
// Contracts by dYdX Foundation. Individual files are released under different licenses.
//
// https://dydx.community
// https://github.com/dydxfoundation/governance-contracts
//
// SPDX-License-Identifier: AGPL-3.0

pragma solidity 0.7.5;
pragma abicoder v2;

import { IERC20 } from '../../interfaces/IERC20.sol';
import { SM1Admin } from './impl/SM1Admin.sol';
import { SM1Getters } from './impl/SM1Getters.sol';
import { SM1Operators } from './impl/SM1Operators.sol';
import { SM1Slashing } from './impl/SM1Slashing.sol';
import { SM1Staking } from './impl/SM1Staking.sol';

/**
 * @title SafetyModuleV1
 * @author dYdX
 *
 * @notice Contract for staking tokens, which may be slashed by the permissioned slasher.
 *
 *  NOTE: Most functions will revert if epoch zero has not started.
 */
contract SafetyModuleV1 is
  SM1Slashing,
  SM1Operators,
  SM1Admin,
  SM1Getters
{
  // ============ Constants ============

  string public constant EIP712_DOMAIN_NAME = 'dYdX Safety Module';

  string public constant EIP712_DOMAIN_VERSION = '1';

  bytes32 public constant EIP712_DOMAIN_SCHEMA_HASH = keccak256(
    'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
  );

  // ============ Constructor ============

  constructor(
    IERC20 stakedToken,
    IERC20 rewardsToken,
    address rewardsTreasury,
    uint256 distributionStart,
    uint256 distributionEnd
  )
    SM1Staking(stakedToken, rewardsToken, rewardsTreasury, distributionStart, distributionEnd)
  {}

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

  function initialize(
    uint256 interval,
    uint256 offset,
    uint256 blackoutWindow
  )
    external
    initializer
  {
    __SM1ExchangeRate_init();
    __SM1Roles_init();
    __SM1EpochSchedule_init(interval, offset, blackoutWindow);
    __SM1Rewards_init();

    // Store the domain separator for EIP-712 signatures.
    uint256 chainId;
    // solium-disable-next-line
    assembly {
      chainId := chainid()
    }
    _DOMAIN_SEPARATOR_ = keccak256(
      abi.encode(
        EIP712_DOMAIN_SCHEMA_HASH,
        keccak256(bytes(EIP712_DOMAIN_NAME)),
        keccak256(bytes(EIP712_DOMAIN_VERSION)),
        chainId,
        address(this)
      )
    );
  }

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

  /**
   * @dev Returns the revision of the implementation contract.
   *
   * @return The revision number.
   */
  function getRevision()
    internal
    pure
    override
    returns (uint256)
  {
    return 1;
  }
}

File 2 of 38 : AccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

import './Context.sol';
import './Strings.sol';
import './ERC165.sol';

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
  function hasRole(bytes32 role, address account) external view returns (bool);

  function getRoleAdmin(bytes32 role) external view returns (bytes32);

  function grantRole(bytes32 role, address account) external;

  function revokeRole(bytes32 role, address account) external;

  function renounceRole(bytes32 role, address account) external;
}

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControlUpgradeable is Context, IAccessControlUpgradeable, ERC165 {
  struct RoleData {
    mapping(address => bool) members;
    bytes32 adminRole;
  }

  mapping(bytes32 => RoleData) private _roles;

  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

  /**
   * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
   *
   * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
   * {RoleAdminChanged} not being emitted signaling this.
   *
   * _Available since v3.1._
   */
  event RoleAdminChanged(
    bytes32 indexed role,
    bytes32 indexed previousAdminRole,
    bytes32 indexed newAdminRole
  );

  /**
   * @dev Emitted when `account` is granted `role`.
   *
   * `sender` is the account that originated the contract call, an admin role
   * bearer except when using {_setupRole}.
   */
  event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

  /**
   * @dev Emitted when `account` is revoked `role`.
   *
   * `sender` is the account that originated the contract call:
   *   - if using `revokeRole`, it is the admin role bearer
   *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
   */
  event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

  /**
   * @dev Modifier that checks that an account has a specific role. Reverts
   * with a standardized message including the required role.
   *
   * The format of the revert reason is given by the following regular expression:
   *
   *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
   *
   * _Available since v4.1._
   */
  modifier onlyRole(bytes32 role) {
    _checkRole(role, _msgSender());
    _;
  }

  /**
   * @dev See {IERC165-supportsInterface}.
   */
  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    return
      interfaceId == type(IAccessControlUpgradeable).interfaceId ||
      super.supportsInterface(interfaceId);
  }

  /**
   * @dev Returns `true` if `account` has been granted `role`.
   */
  function hasRole(bytes32 role, address account) public view override returns (bool) {
    return _roles[role].members[account];
  }

  /**
   * @dev Revert with a standard message if `account` is missing `role`.
   *
   * The format of the revert reason is given by the following regular expression:
   *
   *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
   */
  function _checkRole(bytes32 role, address account) internal view {
    if (!hasRole(role, account)) {
      revert(
        string(
          abi.encodePacked(
            'AccessControl: account ',
            Strings.toHexString(uint160(account), 20),
            ' is missing role ',
            Strings.toHexString(uint256(role), 32)
          )
        )
      );
    }
  }

  /**
   * @dev Returns the admin role that controls `role`. See {grantRole} and
   * {revokeRole}.
   *
   * To change a role's admin, use {_setRoleAdmin}.
   */
  function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
    return _roles[role].adminRole;
  }

  /**
   * @dev Grants `role` to `account`.
   *
   * If `account` had not been already granted `role`, emits a {RoleGranted}
   * event.
   *
   * Requirements:
   *
   * - the caller must have ``role``'s admin role.
   */
  function grantRole(bytes32 role, address account)
    public
    virtual
    override
    onlyRole(getRoleAdmin(role))
  {
    _grantRole(role, account);
  }

  /**
   * @dev Revokes `role` from `account`.
   *
   * If `account` had been granted `role`, emits a {RoleRevoked} event.
   *
   * Requirements:
   *
   * - the caller must have ``role``'s admin role.
   */
  function revokeRole(bytes32 role, address account)
    public
    virtual
    override
    onlyRole(getRoleAdmin(role))
  {
    _revokeRole(role, account);
  }

  /**
   * @dev Revokes `role` from the calling account.
   *
   * Roles are often managed via {grantRole} and {revokeRole}: this function's
   * purpose is to provide a mechanism for accounts to lose their privileges
   * if they are compromised (such as when a trusted device is misplaced).
   *
   * If the calling account had been granted `role`, emits a {RoleRevoked}
   * event.
   *
   * Requirements:
   *
   * - the caller must be `account`.
   */
  function renounceRole(bytes32 role, address account) public virtual override {
    require(account == _msgSender(), 'AccessControl: can only renounce roles for self');

    _revokeRole(role, account);
  }

  /**
   * @dev Grants `role` to `account`.
   *
   * If `account` had not been already granted `role`, emits a {RoleGranted}
   * event. Note that unlike {grantRole}, this function doesn't perform any
   * checks on the calling account.
   *
   * [WARNING]
   * ====
   * This function should only be called from the constructor when setting
   * up the initial roles for the system.
   *
   * Using this function in any other way is effectively circumventing the admin
   * system imposed by {AccessControl}.
   * ====
   */
  function _setupRole(bytes32 role, address account) internal virtual {
    _grantRole(role, account);
  }

  /**
   * @dev Sets `adminRole` as ``role``'s admin role.
   *
   * Emits a {RoleAdminChanged} event.
   */
  function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
    emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
    _roles[role].adminRole = adminRole;
  }

  function _grantRole(bytes32 role, address account) private {
    if (!hasRole(role, account)) {
      _roles[role].members[account] = true;
      emit RoleGranted(role, account, _msgSender());
    }
  }

  function _revokeRole(bytes32 role, address account) private {
    if (hasRole(role, account)) {
      _roles[role].members[account] = false;
      emit RoleRevoked(role, account, _msgSender());
    }
  }

  uint256[49] private __gap;
}

File 3 of 38 : Context.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/*
 * @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 GSN 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 payable) {
    return msg.sender;
  }

  function _msgData() internal view virtual returns (bytes memory) {
    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
    return msg.data;
  }
}

File 4 of 38 : Strings.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/**
 * @dev String operations.
 */
library Strings {
  bytes16 private constant alphabet = '0123456789abcdef';

  /**
   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
   */
  function toString(uint256 value) internal pure returns (string memory) {
    // Inspired by OraclizeAPI's implementation - MIT licence
    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

    if (value == 0) {
      return '0';
    }
    uint256 temp = value;
    uint256 digits;
    while (temp != 0) {
      digits++;
      temp /= 10;
    }
    bytes memory buffer = new bytes(digits);
    while (value != 0) {
      digits -= 1;
      buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
      value /= 10;
    }
    return string(buffer);
  }

  /**
   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
   */
  function toHexString(uint256 value) internal pure returns (string memory) {
    if (value == 0) {
      return '0x00';
    }
    uint256 temp = value;
    uint256 length = 0;
    while (temp != 0) {
      length++;
      temp >>= 8;
    }
    return toHexString(value, length);
  }

  /**
   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
   */
  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
    bytes memory buffer = new bytes(2 * length + 2);
    buffer[0] = '0';
    buffer[1] = 'x';
    for (uint256 i = 2 * length + 1; i > 1; --i) {
      buffer[i] = alphabet[value & 0xf];
      value >>= 4;
    }
    require(value == 0, 'Strings: hex length insufficient');
    return string(buffer);
  }
}

File 5 of 38 : ERC165.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

import './IERC165.sol';

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
  /**
   * @dev See {IERC165-supportsInterface}.
   */
  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    return interfaceId == type(IERC165).interfaceId;
  }
}

File 6 of 38 : IERC165.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
  /**
   * @dev Returns true if this contract implements the interface defined by
   * `interfaceId`. See the corresponding
   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
   * to learn more about how these ids are created.
   *
   * This function call must use less than 30 000 gas.
   */
  function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 7 of 38 : SM1Storage.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import {
  AccessControlUpgradeable
} from '../../../dependencies/open-zeppelin/AccessControlUpgradeable.sol';
import { ReentrancyGuard } from '../../../utils/ReentrancyGuard.sol';
import { VersionedInitializable } from '../../../utils/VersionedInitializable.sol';
import { SM1Types } from '../lib/SM1Types.sol';

/**
 * @title SM1Storage
 * @author dYdX
 *
 * @dev Storage contract. Contains or inherits from all contract with storage.
 */
abstract contract SM1Storage is
  AccessControlUpgradeable,
  ReentrancyGuard,
  VersionedInitializable
{
  // ============ Epoch Schedule ============

  /// @dev The parameters specifying the function from timestamp to epoch number.
  SM1Types.EpochParameters internal _EPOCH_PARAMETERS_;

  /// @dev The period of time at the end of each epoch in which withdrawals cannot be requested.
  uint256 internal _BLACKOUT_WINDOW_;

  // ============ Staked Token ERC20 ============

  /// @dev Allowances for ERC-20 transfers.
  mapping(address => mapping(address => uint256)) internal _ALLOWANCES_;

  // ============ Governance Power Delegation ============

  /// @dev Domain separator for EIP-712 signatures.
  bytes32 internal _DOMAIN_SEPARATOR_;

  /// @dev Mapping from (owner) => (next valid nonce) for EIP-712 signatures.
  mapping(address => uint256) internal _NONCES_;

  /// @dev Snapshots and delegates for governance voting power.
  mapping(address => mapping(uint256 => SM1Types.Snapshot)) internal _VOTING_SNAPSHOTS_;
  mapping(address => uint256) internal _VOTING_SNAPSHOT_COUNTS_;
  mapping(address => address) internal _VOTING_DELEGATES_;

  /// @dev Snapshots and delegates for governance proposition power.
  mapping(address => mapping(uint256 => SM1Types.Snapshot)) internal _PROPOSITION_SNAPSHOTS_;
  mapping(address => uint256) internal _PROPOSITION_SNAPSHOT_COUNTS_;
  mapping(address => address) internal _PROPOSITION_DELEGATES_;

  // ============ Rewards Accounting ============

  /// @dev The emission rate of rewards.
  uint256 internal _REWARDS_PER_SECOND_;

  /// @dev The cumulative rewards earned per staked token. (Shared storage slot.)
  uint224 internal _GLOBAL_INDEX_;

  /// @dev The timestamp at which the global index was last updated. (Shared storage slot.)
  uint32 internal _GLOBAL_INDEX_TIMESTAMP_;

  /// @dev The value of the global index when the user's staked balance was last updated.
  mapping(address => uint256) internal _USER_INDEXES_;

  /// @dev The user's accrued, unclaimed rewards (as of the last update to the user index).
  mapping(address => uint256) internal _USER_REWARDS_BALANCES_;

  /// @dev The value of the global index at the end of a given epoch.
  mapping(uint256 => uint256) internal _EPOCH_INDEXES_;

  // ============ Staker Accounting ============

  /// @dev The active balance by staker.
  mapping(address => SM1Types.StoredBalance) internal _ACTIVE_BALANCES_;

  /// @dev The total active balance of stakers.
  SM1Types.StoredBalance internal _TOTAL_ACTIVE_BALANCE_;

  /// @dev The inactive balance by staker.
  mapping(address => SM1Types.StoredBalance) internal _INACTIVE_BALANCES_;

  /// @dev The total inactive balance of stakers.
  SM1Types.StoredBalance internal _TOTAL_INACTIVE_BALANCE_;

  // ============ Exchange Rate ============

  /// @dev The value of one underlying token, in the units used for staked balances, denominated
  ///  as a mutiple of EXCHANGE_RATE_BASE for additional precision.
  uint256 internal _EXCHANGE_RATE_;

  /// @dev Historical snapshots of the exchange rate, in each block that it has changed.
  mapping(uint256 => SM1Types.Snapshot) internal _EXCHANGE_RATE_SNAPSHOTS_;

  /// @dev Number of snapshots of the exchange rate.
  uint256 internal _EXCHANGE_RATE_SNAPSHOT_COUNT_;
}

File 8 of 38 : ReentrancyGuard.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.7.5;
pragma abicoder v2;

/**
 * @title ReentrancyGuard
 * @author dYdX
 *
 * @dev Updated ReentrancyGuard library designed to be used with Proxy Contracts.
 */
abstract contract ReentrancyGuard {
  uint256 private constant NOT_ENTERED = 1;
  uint256 private constant ENTERED = uint256(int256(-1));

  uint256 private _STATUS_;

  constructor()
    internal
  {
    _STATUS_ = NOT_ENTERED;
  }

  modifier nonReentrant() {
    require(_STATUS_ != ENTERED, 'ReentrancyGuard: reentrant call');
    _STATUS_ = ENTERED;
    _;
    _STATUS_ = NOT_ENTERED;
  }
}

File 9 of 38 : VersionedInitializable.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;

/**
 * @title VersionedInitializable
 * @author Aave, inspired by the OpenZeppelin Initializable contract
 *
 * @dev Helper contract to support initializer functions. To use it, replace
 * the constructor with a function that has the `initializer` modifier.
 * WARNING: Unlike constructors, initializer functions must be manually
 * invoked. This applies both to deploying an Initializable contract, as well
 * as extending an Initializable contract via inheritance.
 * WARNING: When used with inheritance, manual care must be taken to not invoke
 * a parent initializer twice, or ensure that all initializers are idempotent,
 * because this is not dealt with automatically as with constructors.
 *
 */
abstract contract VersionedInitializable {
    /**
   * @dev Indicates that the contract has been initialized.
   */
    uint256 internal lastInitializedRevision = 0;

   /**
   * @dev Modifier to use in the initializer function of a contract.
   */
    modifier initializer() {
        uint256 revision = getRevision();
        require(revision > lastInitializedRevision, "Contract instance has already been initialized");

        lastInitializedRevision = revision;

        _;

    }

    /// @dev returns the revision number of the contract.
    /// Needs to be defined in the inherited class as a constant.
    function getRevision() internal pure virtual returns(uint256);


    // Reserved storage space to allow for layout changes in the future.
    uint256[50] private ______gap;
}

File 10 of 38 : SM1Types.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.7.5;
pragma abicoder v2;

library SM1Types {
  /**
   * @dev The parameters used to convert a timestamp to an epoch number.
   */
  struct EpochParameters {
    uint128 interval;
    uint128 offset;
  }

  /**
   * @dev Snapshot of a value at a specific block, used to track historical governance power.
   */
  struct Snapshot {
    uint256 blockNumber;
    uint256 value;
  }

  /**
   * @dev A balance, possibly with a change scheduled for the next epoch.
   *
   * @param  currentEpoch         The epoch in which the balance was last updated.
   * @param  currentEpochBalance  The balance at epoch `currentEpoch`.
   * @param  nextEpochBalance     The balance at epoch `currentEpoch + 1`.
   */
  struct StoredBalance {
    uint16 currentEpoch;
    uint240 currentEpochBalance;
    uint240 nextEpochBalance;
  }
}

File 11 of 38 : SM1Getters.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { Math } from '../../../utils/Math.sol';
import { SM1Types } from '../lib/SM1Types.sol';
import { SM1Storage } from './SM1Storage.sol';

/**
 * @title SM1Getters
 * @author dYdX
 *
 * @dev Some external getter functions.
 */
abstract contract SM1Getters is
  SM1Storage
{
  using SafeMath for uint256;

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

  /**
   * @notice The parameters specifying the function from timestamp to epoch number.
   *
   * @return The parameters struct with `interval` and `offset` fields.
   */
  function getEpochParameters()
    external
    view
    returns (SM1Types.EpochParameters memory)
  {
    return _EPOCH_PARAMETERS_;
  }

  /**
   * @notice The period of time at the end of each epoch in which withdrawals cannot be requested.
   *
   * @return The blackout window duration, in seconds.
   */
  function getBlackoutWindow()
    external
    view
    returns (uint256)
  {
    return _BLACKOUT_WINDOW_;
  }

  /**
   * @notice Get the domain separator used for EIP-712 signatures.
   *
   * @return The EIP-712 domain separator.
   */
  function getDomainSeparator()
    external
    view
    returns (bytes32)
  {
    return _DOMAIN_SEPARATOR_;
  }

  /**
   * @notice The value of one underlying token, in the units used for staked balances, denominated
   *  as a mutiple of EXCHANGE_RATE_BASE for additional precision.
   *
   *  To convert from an underlying amount to a staked amount, multiply by the exchange rate.
   *
   * @return The exchange rate.
   */
  function getExchangeRate()
    external
    view
    returns (uint256)
  {
    return _EXCHANGE_RATE_;
  }

  /**
   * @notice Get an exchange rate snapshot.
   *
   * @param  index  The index number of the exchange rate snapshot.
   *
   * @return The snapshot struct with `blockNumber` and `value` fields.
   */
  function getExchangeRateSnapshot(
    uint256 index
  )
    external
    view
    returns (SM1Types.Snapshot memory)
  {
    return _EXCHANGE_RATE_SNAPSHOTS_[index];
  }

  /**
   * @notice Get the number of exchange rate snapshots.
   *
   * @return The number of snapshots that have been taken of the exchange rate.
   */
  function getExchangeRateSnapshotCount()
    external
    view
    returns (uint256)
  {
    return _EXCHANGE_RATE_SNAPSHOT_COUNT_;
  }
}

File 12 of 38 : SafeMath.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
  /**
   * @dev Returns the addition of two unsigned integers, reverting on
   * overflow.
   *
   * Counterpart to Solidity's `+` operator.
   *
   * Requirements:
   * - Addition cannot overflow.
   */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a, 'SafeMath: addition overflow');

    return c;
  }

  /**
   * @dev Returns the subtraction of two unsigned integers, reverting on
   * overflow (when the result is negative).
   *
   * Counterpart to Solidity's `-` operator.
   *
   * Requirements:
   * - Subtraction cannot overflow.
   */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    return sub(a, b, 'SafeMath: subtraction overflow');
  }

  /**
   * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
   * overflow (when the result is negative).
   *
   * Counterpart to Solidity's `-` operator.
   *
   * Requirements:
   * - Subtraction cannot overflow.
   */
  function sub(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    require(b <= a, errorMessage);
    uint256 c = a - b;

    return c;
  }

  /**
   * @dev Returns the multiplication of two unsigned integers, reverting on
   * overflow.
   *
   * Counterpart to Solidity's `*` operator.
   *
   * Requirements:
   * - Multiplication cannot overflow.
   */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
    if (a == 0) {
      return 0;
    }

    uint256 c = a * b;
    require(c / a == b, 'SafeMath: multiplication overflow');

    return c;
  }

  /**
   * @dev Returns the integer division of two unsigned integers. Reverts on
   * division by zero. The result is rounded towards zero.
   *
   * Counterpart to Solidity's `/` operator. Note: this function uses a
   * `revert` opcode (which leaves remaining gas untouched) while Solidity
   * uses an invalid opcode to revert (consuming all remaining gas).
   *
   * Requirements:
   * - The divisor cannot be zero.
   */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    return div(a, b, 'SafeMath: division by zero');
  }

  /**
   * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
   * division by zero. The result is rounded towards zero.
   *
   * Counterpart to Solidity's `/` operator. Note: this function uses a
   * `revert` opcode (which leaves remaining gas untouched) while Solidity
   * uses an invalid opcode to revert (consuming all remaining gas).
   *
   * Requirements:
   * - The divisor cannot be zero.
   */
  function div(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    // Solidity only automatically asserts when dividing by 0
    require(b > 0, errorMessage);
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold

    return c;
  }

  /**
   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
   * Reverts when dividing by zero.
   *
   * Counterpart to Solidity's `%` operator. This function uses a `revert`
   * opcode (which leaves remaining gas untouched) while Solidity uses an
   * invalid opcode to revert (consuming all remaining gas).
   *
   * Requirements:
   * - The divisor cannot be zero.
   */
  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    return mod(a, b, 'SafeMath: modulo by zero');
  }

  /**
   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
   * Reverts with custom message when dividing by zero.
   *
   * Counterpart to Solidity's `%` operator. This function uses a `revert`
   * opcode (which leaves remaining gas untouched) while Solidity uses an
   * invalid opcode to revert (consuming all remaining gas).
   *
   * Requirements:
   * - The divisor cannot be zero.
   */
  function mod(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    require(b != 0, errorMessage);
    return a % b;
  }
}

File 13 of 38 : Math.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeMath } from '../dependencies/open-zeppelin/SafeMath.sol';

/**
 * @title Math
 * @author dYdX
 *
 * @dev Library for non-standard Math functions.
 */
library Math {
  using SafeMath for uint256;

  // ============ Library Functions ============

  /**
   * @dev Return `ceil(numerator / denominator)`.
   */
  function divRoundUp(
    uint256 numerator,
    uint256 denominator
  )
    internal
    pure
    returns (uint256)
  {
    if (numerator == 0) {
      // SafeMath will check for zero denominator
      return SafeMath.div(0, denominator);
    }
    return numerator.sub(1).div(denominator).add(1);
  }

  /**
   * @dev Returns the minimum between a and b.
   */
  function min(
    uint256 a,
    uint256 b
  )
    internal
    pure
    returns (uint256)
  {
    return a < b ? a : b;
  }

  /**
   * @dev Returns the maximum between a and b.
   */
  function max(
    uint256 a,
    uint256 b
  )
    internal
    pure
    returns (uint256)
  {
    return a > b ? a : b;
  }
}

File 14 of 38 : IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
  /**
    * @dev Returns the amount of tokens in existence.
    */
  function totalSupply() external view returns (uint256);

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

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

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

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

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

  /**
    * @dev Emitted when `value` tokens are moved from one account (`from`) to
    * another (`to`).
    *
    * Note that `value` may be zero.
    */
  event Transfer(address indexed from, address indexed to, uint256 value);

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

File 15 of 38 : SM1Admin.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { SM1Types } from '../lib/SM1Types.sol';
import { SM1Roles } from './SM1Roles.sol';
import { SM1StakedBalances } from './SM1StakedBalances.sol';

/**
 * @title SM1Admin
 * @author dYdX
 *
 * @dev Admin-only functions.
 */
abstract contract SM1Admin is
  SM1StakedBalances,
  SM1Roles
{
  using SafeMath for uint256;

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

  /**
   * @notice Set the parameters defining the function from timestamp to epoch number.
   *
   *  The formula used is `n = floor((t - b) / a)` where:
   *    - `n` is the epoch number
   *    - `t` is the timestamp (in seconds)
   *    - `b` is a non-negative offset, indicating the start of epoch zero (in seconds)
   *    - `a` is the length of an epoch, a.k.a. the interval (in seconds)
   *
   *  Reverts if epoch zero already started, and the new parameters would change the current epoch.
   *  Reverts if epoch zero has not started, but would have had started under the new parameters.
   *
   * @param  interval  The length `a` of an epoch, in seconds.
   * @param  offset    The offset `b`, i.e. the start of epoch zero, in seconds.
   */
  function setEpochParameters(
    uint256 interval,
    uint256 offset
  )
    external
    onlyRole(EPOCH_PARAMETERS_ROLE)
    nonReentrant
  {
    if (!hasEpochZeroStarted()) {
      require(
        block.timestamp < offset,
        'SM1Admin: Started epoch zero'
      );
      _setEpochParameters(interval, offset);
      return;
    }

    // We must settle the total active balance to ensure the index is recorded at the epoch
    // boundary as needed, before we make any changes to the epoch formula.
    _settleTotalActiveBalance();

    // Update the epoch parameters. Require that the current epoch number is unchanged.
    uint256 originalCurrentEpoch = getCurrentEpoch();
    _setEpochParameters(interval, offset);
    uint256 newCurrentEpoch = getCurrentEpoch();
    require(
      originalCurrentEpoch == newCurrentEpoch,
      'SM1Admin: Changed epochs'
    );
  }

  /**
   * @notice Set the blackout window, during which one cannot request withdrawals of staked funds.
   */
  function setBlackoutWindow(
    uint256 blackoutWindow
  )
    external
    onlyRole(EPOCH_PARAMETERS_ROLE)
    nonReentrant
  {
    _setBlackoutWindow(blackoutWindow);
  }

  /**
   * @notice Set the emission rate of rewards.
   *
   * @param  emissionPerSecond  The new number of rewards tokens given out per second.
   */
  function setRewardsPerSecond(
    uint256 emissionPerSecond
  )
    external
    onlyRole(REWARDS_RATE_ROLE)
    nonReentrant
  {
    uint256 totalStaked = 0;
    if (hasEpochZeroStarted()) {
      // We must settle the total active balance to ensure the index is recorded at the epoch
      // boundary as needed, before we make any changes to the emission rate.
      totalStaked = _settleTotalActiveBalance();
    }
    _setRewardsPerSecond(emissionPerSecond, totalStaked);
  }
}

File 16 of 38 : SM1Operators.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { SM1Roles } from './SM1Roles.sol';
import { SM1Staking } from './SM1Staking.sol';

/**
 * @title SM1Operators
 * @author dYdX
 *
 * @dev Actions which may be called by authorized operators, nominated by the contract owner.
 *
 *  There are two types of operators. These should be smart contracts, which can be used to
 *  provide additional functionality to users:
 *
 *  STAKE_OPERATOR_ROLE:
 *
 *    This operator is allowed to request withdrawals and withdraw funds on behalf of stakers. This
 *    role could be used by a smart contract to provide a staking interface with additional
 *    features, for example, optional lock-up periods that pay out additional rewards (from a
 *    separate rewards pool).
 *
 *  CLAIM_OPERATOR_ROLE:
 *
 *    This operator is allowed to claim rewards on behalf of stakers. This role could be used by a
 *    smart contract to provide an interface for claiming rewards from multiple incentive programs
 *    at once.
 */
abstract contract SM1Operators is
  SM1Staking,
  SM1Roles
{
  using SafeMath for uint256;

  // ============ Events ============

  event OperatorStakedFor(
    address indexed staker,
    uint256 amount,
    address operator
  );

  event OperatorWithdrawalRequestedFor(
    address indexed staker,
    uint256 amount,
    address operator
  );

  event OperatorWithdrewStakeFor(
    address indexed staker,
    address recipient,
    uint256 amount,
    address operator
  );

  event OperatorClaimedRewardsFor(
    address indexed staker,
    address recipient,
    uint256 claimedRewards,
    address operator
  );

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

  /**
   * @notice Request a withdrawal on behalf of a staker.
   *
   *  Reverts if we are currently in the blackout window.
   *
   * @param  staker       The staker whose stake to request a withdrawal for.
   * @param  stakeAmount  The amount of stake to move from the active to the inactive balance.
   */
  function requestWithdrawalFor(
    address staker,
    uint256 stakeAmount
  )
    external
    onlyRole(STAKE_OPERATOR_ROLE)
    nonReentrant
  {
    _requestWithdrawal(staker, stakeAmount);
    emit OperatorWithdrawalRequestedFor(staker, stakeAmount, msg.sender);
  }

  /**
   * @notice Withdraw a staker's stake, and send to the specified recipient.
   *
   * @param  staker       The staker whose stake to withdraw.
   * @param  recipient    The address that should receive the funds.
   * @param  stakeAmount  The amount of stake to withdraw from the staker's inactive balance.
   */
  function withdrawStakeFor(
    address staker,
    address recipient,
    uint256 stakeAmount
  )
    external
    onlyRole(STAKE_OPERATOR_ROLE)
    nonReentrant
  {
    _withdrawStake(staker, recipient, stakeAmount);
    emit OperatorWithdrewStakeFor(staker, recipient, stakeAmount, msg.sender);
  }

  /**
   * @notice Claim rewards on behalf of a staker, and send them to the specified recipient.
   *
   * @param  staker     The staker whose rewards to claim.
   * @param  recipient  The address that should receive the funds.
   *
   * @return The number of rewards tokens claimed.
   */
  function claimRewardsFor(
    address staker,
    address recipient
  )
    external
    onlyRole(CLAIM_OPERATOR_ROLE)
    nonReentrant
    returns (uint256)
  {
    uint256 rewards = _settleAndClaimRewards(staker, recipient); // Emits an event internally.
    emit OperatorClaimedRewardsFor(staker, recipient, rewards, msg.sender);
    return rewards;
  }
}

File 17 of 38 : SM1Slashing.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeERC20 } from '../../../dependencies/open-zeppelin/SafeERC20.sol';
import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { IERC20 } from '../../../interfaces/IERC20.sol';
import { Math } from '../../../utils/Math.sol';
import { SM1Types } from '../lib/SM1Types.sol';
import { SM1Roles } from './SM1Roles.sol';
import { SM1Staking } from './SM1Staking.sol';

/**
 * @title SM1Slashing
 * @author dYdX
 *
 * @dev Provides the slashing function for removing funds from the contract.
 *
 *  SLASHING:
 *
 *   All funds in the contract, active or inactive, are slashable. Slashes are recorded by updating
 *   the exchange rate, and to simplify the technical implementation, we disallow full slashes.
 *   To reduce the possibility of overflow in the exchange rate, we place an upper bound on the
 *   fraction of funds that may be slashed in a single slash.
 *
 *   Warning: Slashing is not possible if the slash would cause the exchange rate to overflow.
 *
 *  REWARDS AND GOVERNANCE POWER ACCOUNTING:
 *
 *   Since all slashes are accounted for by a global exchange rate, slashes do not require any
 *   update to staked balances. The earning of rewards is unaffected by slashes.
 *
 *   Governance power takes slashes into account by using snapshots of the exchange rate inside
 *   the getPowerAtBlock() function. Note that getPowerAtBlock() returns the governance power as of
 *   the end of the specified block.
 */
abstract contract SM1Slashing is
  SM1Staking,
  SM1Roles
{
  using SafeERC20 for IERC20;
  using SafeMath for uint256;

  // ============ Constants ============

  /// @notice The maximum fraction of funds that may be slashed in a single slash (numerator).
  uint256 public constant MAX_SLASH_NUMERATOR = 95;

  /// @notice The maximum fraction of funds that may be slashed in a single slash (denominator).
  uint256 public constant MAX_SLASH_DENOMINATOR = 100;

  // ============ Events ============

  event Slashed(
    uint256 amount,
    address recipient,
    uint256 newExchangeRate
  );

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

  /**
   * @notice Slash staked token balances and withdraw those funds to the specified address.
   *
   * @param  requestedSlashAmount  The request slash amount, denominated in the underlying token.
   * @param  recipient             The address to receive the slashed tokens.
   *
   * @return The amount slashed, denominated in the underlying token.
   */
  function slash(
    uint256 requestedSlashAmount,
    address recipient
  )
    external
    onlyRole(SLASHER_ROLE)
    nonReentrant
    returns (uint256)
  {
    uint256 underlyingBalance = STAKED_TOKEN.balanceOf(address(this));

    if (underlyingBalance == 0) {
      return 0;
    }

    // Get the slash amount and remaining amount. Note that remainingAfterSlash is nonzero.
    uint256 maxSlashAmount = underlyingBalance.mul(MAX_SLASH_NUMERATOR).div(MAX_SLASH_DENOMINATOR);
    uint256 slashAmount = Math.min(requestedSlashAmount, maxSlashAmount);
    uint256 remainingAfterSlash = underlyingBalance.sub(slashAmount);

    if (slashAmount == 0) {
      return 0;
    }

    // Update the exchange rate.
    //
    // Warning: Can revert if the max exchange rate is exceeded.
    uint256 newExchangeRate = updateExchangeRate(underlyingBalance, remainingAfterSlash);

    // Transfer the slashed token.
    STAKED_TOKEN.safeTransfer(recipient, slashAmount);

    emit Slashed(slashAmount, recipient, newExchangeRate);
    return slashAmount;
  }
}

File 18 of 38 : SM1Staking.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeERC20 } from '../../../dependencies/open-zeppelin/SafeERC20.sol';
import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { IERC20 } from '../../../interfaces/IERC20.sol';
import { Math } from '../../../utils/Math.sol';
import { SM1Types } from '../lib/SM1Types.sol';
import { SM1ERC20 } from './SM1ERC20.sol';
import { SM1StakedBalances } from './SM1StakedBalances.sol';

/**
 * @title SM1Staking
 * @author dYdX
 *
 * @dev External functions for stakers. See SM1StakedBalances for details on staker accounting.
 *
 *  UNDERLYING AND STAKED AMOUNTS:
 *
 *   We distinguish between underlying amounts and stake amounts. An underlying amount is denoted
 *   in the original units of the token being staked. A stake amount is adjusted by the exchange
 *   rate, which can increase due to slashing. Before any slashes have occurred, the exchange rate
 *   is equal to one.
 */
abstract contract SM1Staking is
  SM1StakedBalances,
  SM1ERC20
{
  using SafeERC20 for IERC20;
  using SafeMath for uint256;

  // ============ Events ============

  event Staked(
    address indexed staker,
    address spender,
    uint256 underlyingAmount,
    uint256 stakeAmount
  );

  event WithdrawalRequested(
    address indexed staker,
    uint256 stakeAmount
  );

  event WithdrewStake(
    address indexed staker,
    address recipient,
    uint256 underlyingAmount,
    uint256 stakeAmount
  );

  // ============ Constants ============

  IERC20 public immutable STAKED_TOKEN;

  // ============ Constructor ============

  constructor(
    IERC20 stakedToken,
    IERC20 rewardsToken,
    address rewardsTreasury,
    uint256 distributionStart,
    uint256 distributionEnd
  )
    SM1StakedBalances(rewardsToken, rewardsTreasury, distributionStart, distributionEnd)
  {
    STAKED_TOKEN = stakedToken;
  }

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

  /**
   * @notice Deposit and stake funds. These funds are active and start earning rewards immediately.
   *
   * @param  underlyingAmount  The amount of underlying token to stake.
   */
  function stake(
    uint256 underlyingAmount
  )
    external
    nonReentrant
  {
    _stake(msg.sender, underlyingAmount);
  }

  /**
   * @notice Deposit and stake on behalf of another address.
   *
   * @param  staker            The staker who will receive the stake.
   * @param  underlyingAmount  The amount of underlying token to stake.
   */
  function stakeFor(
    address staker,
    uint256 underlyingAmount
  )
    external
    nonReentrant
  {
    _stake(staker, underlyingAmount);
  }

  /**
   * @notice Request to withdraw funds. Starting in the next epoch, the funds will be “inactive”
   *  and available for withdrawal. Inactive funds do not earn rewards.
   *
   *  Reverts if we are currently in the blackout window.
   *
   * @param  stakeAmount  The amount of stake to move from the active to the inactive balance.
   */
  function requestWithdrawal(
    uint256 stakeAmount
  )
    external
    nonReentrant
  {
    _requestWithdrawal(msg.sender, stakeAmount);
  }

  /**
   * @notice Withdraw the sender's inactive funds, and send to the specified recipient.
   *
   * @param  recipient    The address that should receive the funds.
   * @param  stakeAmount  The amount of stake to withdraw from the sender's inactive balance.
   */
  function withdrawStake(
    address recipient,
    uint256 stakeAmount
  )
    external
    nonReentrant
  {
    _withdrawStake(msg.sender, recipient, stakeAmount);
  }

  /**
   * @notice Withdraw the max available inactive funds, and send to the specified recipient.
   *
   *  This is less gas-efficient than querying the max via eth_call and calling withdrawStake().
   *
   * @param  recipient  The address that should receive the funds.
   *
   * @return The withdrawn amount.
   */
  function withdrawMaxStake(
    address recipient
  )
    external
    nonReentrant
    returns (uint256)
  {
    uint256 stakeAmount = getStakeAvailableToWithdraw(msg.sender);
    _withdrawStake(msg.sender, recipient, stakeAmount);
    return stakeAmount;
  }

  /**
   * @notice Settle and claim all rewards, and send them to the specified recipient.
   *
   *  Call this function with eth_call to query the claimable rewards balance.
   *
   * @param  recipient  The address that should receive the funds.
   *
   * @return The number of rewards tokens claimed.
   */
  function claimRewards(
    address recipient
  )
    external
    nonReentrant
    returns (uint256)
  {
    return _settleAndClaimRewards(msg.sender, recipient); // Emits an event internally.
  }

  // ============ Public Functions ============

  /**
   * @notice Get the amount of stake available for a given staker to withdraw.
   *
   * @param  staker  The address whose balance to check.
   *
   * @return The staker's stake amount that is inactive and available to withdraw.
   */
  function getStakeAvailableToWithdraw(
    address staker
  )
    public
    view
    returns (uint256)
  {
    // Note that the next epoch inactive balance is always at least that of the current epoch.
    return getInactiveBalanceCurrentEpoch(staker);
  }

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

  function _stake(
    address staker,
    uint256 underlyingAmount
  )
    internal
  {
    // Convert using the exchange rate.
    uint256 stakeAmount = stakeAmountFromUnderlyingAmount(underlyingAmount);

    // Update staked balances and delegate snapshots.
    _increaseCurrentAndNextActiveBalance(staker, stakeAmount);
    _moveDelegatesForTransfer(address(0), staker, stakeAmount);

    // Transfer token from the sender.
    STAKED_TOKEN.safeTransferFrom(msg.sender, address(this), underlyingAmount);

    emit Staked(staker, msg.sender, underlyingAmount, stakeAmount);
    emit Transfer(address(0), msg.sender, stakeAmount);
  }

  function _requestWithdrawal(
    address staker,
    uint256 stakeAmount
  )
    internal
  {
    require(
      !inBlackoutWindow(),
      'SM1Staking: Withdraw requests restricted in the blackout window'
    );

    // Get the staker's requestable amount and revert if there is not enough to request withdrawal.
    uint256 requestableBalance = getActiveBalanceNextEpoch(staker);
    require(
      stakeAmount <= requestableBalance,
      'SM1Staking: Withdraw request exceeds next active balance'
    );

    // Move amount from active to inactive in the next epoch.
    _moveNextBalanceActiveToInactive(staker, stakeAmount);

    emit WithdrawalRequested(staker, stakeAmount);
  }

  function _withdrawStake(
    address staker,
    address recipient,
    uint256 stakeAmount
  )
    internal
  {
    // Get staker withdrawable balance and revert if there is not enough to withdraw.
    uint256 withdrawableBalance = getInactiveBalanceCurrentEpoch(staker);
    require(
      stakeAmount <= withdrawableBalance,
      'SM1Staking: Withdraw amount exceeds staker inactive balance'
    );

    // Update staked balances and delegate snapshots.
    _decreaseCurrentAndNextInactiveBalance(staker, stakeAmount);
    _moveDelegatesForTransfer(staker, address(0), stakeAmount);

    // Convert using the exchange rate.
    uint256 underlyingAmount = underlyingAmountFromStakeAmount(stakeAmount);

    // Transfer token to the recipient.
    STAKED_TOKEN.safeTransfer(recipient, underlyingAmount);

    emit Transfer(msg.sender, address(0), stakeAmount);
    emit WithdrewStake(staker, recipient, underlyingAmount, stakeAmount);
  }
}

File 19 of 38 : SM1Roles.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SM1Storage } from './SM1Storage.sol';

/**
 * @title SM1Roles
 * @author dYdX
 *
 * @dev Defines roles used in the SafetyModuleV1 contract. The hierarchy of roles and powers
 *  of each role are described below.
 *
 *  Roles:
 *
 *    OWNER_ROLE
 *      | -> May add or remove addresses from any of the roles below.
 *      |
 *      +-- SLASHER_ROLE
 *      |     -> Can slash staked token balances and withdraw those funds.
 *      |
 *      +-- EPOCH_PARAMETERS_ROLE
 *      |     -> May set epoch parameters such as the interval, offset, and blackout window.
 *      |
 *      +-- REWARDS_RATE_ROLE
 *      |     -> May set the emission rate of rewards.
 *      |
 *      +-- CLAIM_OPERATOR_ROLE
 *      |     -> May claim rewards on behalf of a user.
 *      |
 *      +-- STAKE_OPERATOR_ROLE
 *            -> May manipulate user's staked funds (e.g. perform withdrawals on behalf of a user).
 */
abstract contract SM1Roles is SM1Storage {
  bytes32 public constant OWNER_ROLE = keccak256('OWNER_ROLE');
  bytes32 public constant SLASHER_ROLE = keccak256('SLASHER_ROLE');
  bytes32 public constant EPOCH_PARAMETERS_ROLE = keccak256('EPOCH_PARAMETERS_ROLE');
  bytes32 public constant REWARDS_RATE_ROLE = keccak256('REWARDS_RATE_ROLE');
  bytes32 public constant CLAIM_OPERATOR_ROLE = keccak256('CLAIM_OPERATOR_ROLE');
  bytes32 public constant STAKE_OPERATOR_ROLE = keccak256('STAKE_OPERATOR_ROLE');

  function __SM1Roles_init() internal {
    // Assign roles to the sender.
    //
    // The STAKE_OPERATOR_ROLE and CLAIM_OPERATOR_ROLE roles are not initially assigned.
    // These can be assigned to other smart contracts to provide additional functionality for users.
    _setupRole(OWNER_ROLE, msg.sender);
    _setupRole(SLASHER_ROLE, msg.sender);
    _setupRole(EPOCH_PARAMETERS_ROLE, msg.sender);
    _setupRole(REWARDS_RATE_ROLE, msg.sender);

    // Set OWNER_ROLE as the admin of all roles.
    _setRoleAdmin(OWNER_ROLE, OWNER_ROLE);
    _setRoleAdmin(SLASHER_ROLE, OWNER_ROLE);
    _setRoleAdmin(EPOCH_PARAMETERS_ROLE, OWNER_ROLE);
    _setRoleAdmin(REWARDS_RATE_ROLE, OWNER_ROLE);
    _setRoleAdmin(CLAIM_OPERATOR_ROLE, OWNER_ROLE);
    _setRoleAdmin(STAKE_OPERATOR_ROLE, OWNER_ROLE);
  }
}

File 20 of 38 : SM1StakedBalances.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { IERC20 } from '../../../interfaces/IERC20.sol';
import { SafeCast } from '../lib/SafeCast.sol';
import { SM1Types } from '../lib/SM1Types.sol';
import { SM1Rewards } from './SM1Rewards.sol';

/**
 * @title SM1StakedBalances
 * @author dYdX
 *
 * @dev Accounting of staked balances.
 *
 *  NOTE: Functions may revert if epoch zero has not started.
 *
 *  NOTE: All amounts dealt with in this file are denominated in staked units, which because of the
 *   exchange rate, may not correspond one-to-one with the underlying token. See SM1Staking.sol.
 *
 *  STAKED BALANCE ACCOUNTING:
 *
 *   A staked balance is in one of two states:
 *     - active: Earning staking rewards; cannot be withdrawn by staker; may be slashed.
 *     - inactive: Not earning rewards; can be withdrawn by the staker; may be slashed.
 *
 *   A staker may have a combination of active and inactive balances. The following operations
 *   affect staked balances as follows:
 *     - deposit:            Increase active balance.
 *     - request withdrawal: At the end of the current epoch, move some active funds to inactive.
 *     - withdraw:           Decrease inactive balance.
 *     - transfer:           Move some active funds to another staker.
 *
 *   To encode the fact that a balance may be scheduled to change at the end of a certain epoch, we
 *   store each balance as a struct of three fields: currentEpoch, currentEpochBalance, and
 *   nextEpochBalance.
 *
 *  REWARDS ACCOUNTING:
 *
 *   Active funds earn rewards for the period of time that they remain active. This means, after
 *   requesting a withdrawal of some funds, those funds will continue to earn rewards until the end
 *   of the epoch. For example:
 *
 *     epoch: n        n + 1      n + 2      n + 3
 *            |          |          |          |
 *            +----------+----------+----------+-----...
 *               ^ t_0: User makes a deposit.
 *                          ^ t_1: User requests a withdrawal of all funds.
 *                                  ^ t_2: The funds change state from active to inactive.
 *
 *   In the above scenario, the user would earn rewards for the period from t_0 to t_2, varying
 *   with the total staked balance in that period. If the user only request a withdrawal for a part
 *   of their balance, then the remaining balance would continue earning rewards beyond t_2.
 *
 *   User rewards must be settled via SM1Rewards any time a user's active balance changes. Special
 *   attention is paid to the the epoch boundaries, where funds may have transitioned from active
 *   to inactive.
 *
 *  SETTLEMENT DETAILS:
 *
 *   Internally, this module uses the following types of operations on stored balances:
 *     - Load:            Loads a balance, while applying settlement logic internally to get the
 *                        up-to-date result. Returns settlement results without updating state.
 *     - Store:           Stores a balance.
 *     - Load-for-update: Performs a load and applies updates as needed to rewards accounting.
 *                        Since this is state-changing, it must be followed by a store operation.
 *     - Settle:          Performs load-for-update and store operations.
 *
 *   This module is responsible for maintaining the following invariants to ensure rewards are
 *   calculated correctly:
 *     - When an active balance is loaded for update, if a rollover occurs from one epoch to the
 *       next, the rewards index must be settled up to the boundary at which the rollover occurs.
 *     - Because the global rewards index is needed to update the user rewards index, the total
 *       active balance must be settled before any staker balances are settled or loaded for update.
 *     - A staker's balance must be settled before their rewards are settled.
 */
abstract contract SM1StakedBalances is
  SM1Rewards
{
  using SafeCast for uint256;
  using SafeMath for uint256;

  // ============ Constructor ============

  constructor(
    IERC20 rewardsToken,
    address rewardsTreasury,
    uint256 distributionStart,
    uint256 distributionEnd
  )
    SM1Rewards(rewardsToken, rewardsTreasury, distributionStart, distributionEnd)
  {}

  // ============ Public Functions ============

  /**
   * @notice Get the current active balance of a staker.
   */
  function getActiveBalanceCurrentEpoch(
    address staker
  )
    public
    view
    returns (uint256)
  {
    if (!hasEpochZeroStarted()) {
      return 0;
    }
    (SM1Types.StoredBalance memory balance, , , ) = _loadActiveBalance(
      _ACTIVE_BALANCES_[staker]
    );
    return uint256(balance.currentEpochBalance);
  }

  /**
   * @notice Get the next epoch active balance of a staker.
   */
  function getActiveBalanceNextEpoch(
    address staker
  )
    public
    view
    returns (uint256)
  {
    if (!hasEpochZeroStarted()) {
      return 0;
    }
    (SM1Types.StoredBalance memory balance, , , ) = _loadActiveBalance(
      _ACTIVE_BALANCES_[staker]
    );
    return uint256(balance.nextEpochBalance);
  }

  /**
   * @notice Get the current total active balance.
   */
  function getTotalActiveBalanceCurrentEpoch()
    public
    view
    returns (uint256)
  {
    if (!hasEpochZeroStarted()) {
      return 0;
    }
    (SM1Types.StoredBalance memory balance, , , ) = _loadActiveBalance(
      _TOTAL_ACTIVE_BALANCE_
    );
    return uint256(balance.currentEpochBalance);
  }

  /**
   * @notice Get the next epoch total active balance.
   */
  function getTotalActiveBalanceNextEpoch()
    public
    view
    returns (uint256)
  {
    if (!hasEpochZeroStarted()) {
      return 0;
    }
    (SM1Types.StoredBalance memory balance, , , ) = _loadActiveBalance(
      _TOTAL_ACTIVE_BALANCE_
    );
    return uint256(balance.nextEpochBalance);
  }

  /**
   * @notice Get the current inactive balance of a staker.
   * @dev The balance is converted via the index to token units.
   */
  function getInactiveBalanceCurrentEpoch(
    address staker
  )
    public
    view
    returns (uint256)
  {
    if (!hasEpochZeroStarted()) {
      return 0;
    }
    SM1Types.StoredBalance memory balance = _loadInactiveBalance(_INACTIVE_BALANCES_[staker]);
    return uint256(balance.currentEpochBalance);
  }

  /**
   * @notice Get the next epoch inactive balance of a staker.
   * @dev The balance is converted via the index to token units.
   */
  function getInactiveBalanceNextEpoch(
    address staker
  )
    public
    view
    returns (uint256)
  {
    if (!hasEpochZeroStarted()) {
      return 0;
    }
    SM1Types.StoredBalance memory balance = _loadInactiveBalance(_INACTIVE_BALANCES_[staker]);
    return uint256(balance.nextEpochBalance);
  }

  /**
   * @notice Get the current total inactive balance.
   */
  function getTotalInactiveBalanceCurrentEpoch()
    public
    view
    returns (uint256)
  {
    if (!hasEpochZeroStarted()) {
      return 0;
    }
    SM1Types.StoredBalance memory balance = _loadInactiveBalance(_TOTAL_INACTIVE_BALANCE_);
    return uint256(balance.currentEpochBalance);
  }

  /**
   * @notice Get the next epoch total inactive balance.
   */
  function getTotalInactiveBalanceNextEpoch()
    public
    view
    returns (uint256)
  {
    if (!hasEpochZeroStarted()) {
      return 0;
    }
    SM1Types.StoredBalance memory balance = _loadInactiveBalance(_TOTAL_INACTIVE_BALANCE_);
    return uint256(balance.nextEpochBalance);
  }

  /**
   * @notice Get the current transferable balance for a user. The user can
   *  only transfer their balance that is not currently inactive or going to be
   *  inactive in the next epoch. Note that this means the user's transferable funds
   *  are their active balance of the next epoch.
   *
   * @param  account  The account to get the transferable balance of.
   *
   * @return The user's transferable balance.
   */
  function getTransferableBalance(
    address account
  )
    public
    view
    returns (uint256)
  {
    return getActiveBalanceNextEpoch(account);
  }

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

  function _increaseCurrentAndNextActiveBalance(
    address staker,
    uint256 amount
  )
    internal
  {
    // Always settle total active balance before settling a staker active balance.
    uint256 oldTotalBalance = _increaseCurrentAndNextBalances(address(0), true, amount);
    uint256 oldUserBalance = _increaseCurrentAndNextBalances(staker, true, amount);

    // When an active balance changes at current timestamp, settle rewards to the current timestamp.
    _settleUserRewardsUpToNow(staker, oldUserBalance, oldTotalBalance);
  }

  function _moveNextBalanceActiveToInactive(
    address staker,
    uint256 amount
  )
    internal
  {
    // Decrease the active balance for the next epoch.
    // Always settle total active balance before settling a staker active balance.
    _decreaseNextBalance(address(0), true, amount);
    _decreaseNextBalance(staker, true, amount);

    // Increase the inactive balance for the next epoch.
    _increaseNextBalance(address(0), false, amount);
    _increaseNextBalance(staker, false, amount);

    // Note that we don't need to settle rewards since the current active balance did not change.
  }

  function _transferCurrentAndNextActiveBalance(
    address sender,
    address recipient,
    uint256 amount
  )
    internal
  {
    // Always settle total active balance before settling a staker active balance.
    uint256 totalBalance = _settleTotalActiveBalance();

    // Move current and next active balances from sender to recipient.
    uint256 oldSenderBalance = _decreaseCurrentAndNextBalances(sender, true, amount);
    uint256 oldRecipientBalance = _increaseCurrentAndNextBalances(recipient, true, amount);

    // When an active balance changes at current timestamp, settle rewards to the current timestamp.
    _settleUserRewardsUpToNow(sender, oldSenderBalance, totalBalance);
    _settleUserRewardsUpToNow(recipient, oldRecipientBalance, totalBalance);
  }

  function _decreaseCurrentAndNextInactiveBalance(
    address staker,
    uint256 amount
  )
    internal
  {
    // Decrease the inactive balance for the next epoch.
    _decreaseCurrentAndNextBalances(address(0), false, amount);
    _decreaseCurrentAndNextBalances(staker, false, amount);

    // Note that we don't settle rewards since active balances are not affected.
  }

  function _settleTotalActiveBalance()
    internal
    returns (uint256)
  {
    return _settleBalance(address(0), true);
  }

  function _settleAndClaimRewards(
    address staker,
    address recipient
  )
    internal
    returns (uint256)
  {
    // Always settle total active balance before settling a staker active balance.
    uint256 totalBalance = _settleTotalActiveBalance();

    // Always settle staker active balance before settling staker rewards.
    uint256 userBalance = _settleBalance(staker, true);

    // Settle rewards balance since we want to claim the full accrued amount.
    _settleUserRewardsUpToNow(staker, userBalance, totalBalance);

    // Claim rewards balance.
    return _claimRewards(staker, recipient);
  }

  // ============ Private Functions ============

  /**
   * @dev Load a balance for update and then store it.
   */
  function _settleBalance(
    address maybeStaker,
    bool isActiveBalance
  )
    private
    returns (uint256)
  {
    SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance);
    SM1Types.StoredBalance memory balance =
      _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance);

    uint256 currentBalance = uint256(balance.currentEpochBalance);

    _storeBalance(balancePtr, balance);
    return currentBalance;
  }

  /**
   * @dev Settle a balance while applying an increase.
   */
  function _increaseCurrentAndNextBalances(
    address maybeStaker,
    bool isActiveBalance,
    uint256 amount
  )
    private
    returns (uint256)
  {
    SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance);
    SM1Types.StoredBalance memory balance =
      _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance);

    uint256 originalCurrentBalance = uint256(balance.currentEpochBalance);
    balance.currentEpochBalance = originalCurrentBalance.add(amount).toUint240();
    balance.nextEpochBalance = uint256(balance.nextEpochBalance).add(amount).toUint240();

    _storeBalance(balancePtr, balance);
    return originalCurrentBalance;
  }

  /**
   * @dev Settle a balance while applying a decrease.
   */
  function _decreaseCurrentAndNextBalances(
    address maybeStaker,
    bool isActiveBalance,
    uint256 amount
  )
    private
    returns (uint256)
  {
    SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance);
    SM1Types.StoredBalance memory balance =
      _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance);

    uint256 originalCurrentBalance = uint256(balance.currentEpochBalance);
    balance.currentEpochBalance = originalCurrentBalance.sub(amount).toUint240();
    balance.nextEpochBalance = uint256(balance.nextEpochBalance).sub(amount).toUint240();

    _storeBalance(balancePtr, balance);
    return originalCurrentBalance;
  }

  /**
   * @dev Settle a balance while applying an increase.
   */
  function _increaseNextBalance(
    address maybeStaker,
    bool isActiveBalance,
    uint256 amount
  )
    private
  {
    SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance);
    SM1Types.StoredBalance memory balance =
      _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance);

    balance.nextEpochBalance = uint256(balance.nextEpochBalance).add(amount).toUint240();

    _storeBalance(balancePtr, balance);
  }

  /**
   * @dev Settle a balance while applying a decrease.
   */
  function _decreaseNextBalance(
    address maybeStaker,
    bool isActiveBalance,
    uint256 amount
  )
    private
  {
    SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance);
    SM1Types.StoredBalance memory balance =
      _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance);

    balance.nextEpochBalance = uint256(balance.nextEpochBalance).sub(amount).toUint240();

    _storeBalance(balancePtr, balance);
  }

  function _getBalancePtr(
    address maybeStaker,
    bool isActiveBalance
  )
    private
    view
    returns (SM1Types.StoredBalance storage)
  {
    // Active.
    if (isActiveBalance) {
      if (maybeStaker != address(0)) {
        return _ACTIVE_BALANCES_[maybeStaker];
      }
      return _TOTAL_ACTIVE_BALANCE_;
    }

    // Inactive.
    if (maybeStaker != address(0)) {
      return _INACTIVE_BALANCES_[maybeStaker];
    }
    return _TOTAL_INACTIVE_BALANCE_;
  }

  /**
   * @dev Load a balance for updating.
   *
   *  IMPORTANT: This function may modify state, and so the balance MUST be stored afterwards.
   *    - For active balances:
   *      - If a rollover occurs, rewards are settled up to the epoch boundary.
   *
   * @param  balancePtr       A storage pointer to the balance.
   * @param  maybeStaker      The user address, or address(0) to update total balance.
   * @param  isActiveBalance  Whether the balance is an active balance.
   */
  function _loadBalanceForUpdate(
    SM1Types.StoredBalance storage balancePtr,
    address maybeStaker,
    bool isActiveBalance
  )
    private
    returns (SM1Types.StoredBalance memory)
  {
    // Active balance.
    if (isActiveBalance) {
      (
        SM1Types.StoredBalance memory balance,
        uint256 beforeRolloverEpoch,
        uint256 beforeRolloverBalance,
        bool didRolloverOccur
      ) = _loadActiveBalance(balancePtr);
      if (didRolloverOccur) {
        // Handle the effect of the balance rollover on rewards. We must partially settle the index
        // up to the epoch boundary where the change in balance occurred. We pass in the balance
        // from before the boundary.
        if (maybeStaker == address(0)) {
          // If it's the total active balance...
          _settleGlobalIndexUpToEpoch(beforeRolloverBalance, beforeRolloverEpoch);
        } else {
          // If it's a user active balance...
          _settleUserRewardsUpToEpoch(maybeStaker, beforeRolloverBalance, beforeRolloverEpoch);
        }
      }
      return balance;
    }

    // Inactive balance.
    return _loadInactiveBalance(balancePtr);
  }

  function _loadActiveBalance(
    SM1Types.StoredBalance storage balancePtr
  )
    private
    view
    returns (
      SM1Types.StoredBalance memory,
      uint256,
      uint256,
      bool
    )
  {
    SM1Types.StoredBalance memory balance = balancePtr;

    // Return these as they may be needed for rewards settlement.
    uint256 beforeRolloverEpoch = uint256(balance.currentEpoch);
    uint256 beforeRolloverBalance = uint256(balance.currentEpochBalance);
    bool didRolloverOccur = false;

    // Roll the balance forward if needed.
    uint256 currentEpoch = getCurrentEpoch();
    if (currentEpoch > uint256(balance.currentEpoch)) {
      didRolloverOccur = balance.currentEpochBalance != balance.nextEpochBalance;

      balance.currentEpoch = currentEpoch.toUint16();
      balance.currentEpochBalance = balance.nextEpochBalance;
    }

    return (balance, beforeRolloverEpoch, beforeRolloverBalance, didRolloverOccur);
  }

  function _loadInactiveBalance(
    SM1Types.StoredBalance storage balancePtr
  )
    private
    view
    returns (SM1Types.StoredBalance memory)
  {
    SM1Types.StoredBalance memory balance = balancePtr;

    // Roll the balance forward if needed.
    uint256 currentEpoch = getCurrentEpoch();
    if (currentEpoch > uint256(balance.currentEpoch)) {
      balance.currentEpoch = currentEpoch.toUint16();
      balance.currentEpochBalance = balance.nextEpochBalance;
    }

    return balance;
  }

  /**
   * @dev Store a balance.
   */
  function _storeBalance(
    SM1Types.StoredBalance storage balancePtr,
    SM1Types.StoredBalance memory balance
  )
    private
  {
    // Note: This should use a single `sstore` when compiler optimizations are enabled.
    balancePtr.currentEpoch = balance.currentEpoch;
    balancePtr.currentEpochBalance = balance.currentEpochBalance;
    balancePtr.nextEpochBalance = balance.nextEpochBalance;
  }
}

File 21 of 38 : SafeCast.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.7.5;
pragma abicoder v2;

/**
 * @dev Methods for downcasting unsigned integers, reverting on overflow.
 */
library SafeCast {

  /**
   * @dev Downcast to a uint16, reverting on overflow.
   */
  function toUint16(
    uint256 a
  )
    internal
    pure
    returns (uint16)
  {
    uint16 b = uint16(a);
    require(
      uint256(b) == a,
      'SafeCast: toUint16 overflow'
    );
    return b;
  }

  /**
   * @dev Downcast to a uint32, reverting on overflow.
   */
  function toUint32(
    uint256 a
  )
    internal
    pure
    returns (uint32)
  {
    uint32 b = uint32(a);
    require(
      uint256(b) == a,
      'SafeCast: toUint32 overflow'
    );
    return b;
  }

  /**
   * @dev Downcast to a uint128, reverting on overflow.
   */
  function toUint128(
    uint256 a
  )
    internal
    pure
    returns (uint128)
  {
    uint128 b = uint128(a);
    require(
      uint256(b) == a,
      'SafeCast: toUint128 overflow'
    );
    return b;
  }

  /**
   * @dev Downcast to a uint224, reverting on overflow.
   */
  function toUint224(
    uint256 a
  )
    internal
    pure
    returns (uint224)
  {
    uint224 b = uint224(a);
    require(
      uint256(b) == a,
      'SafeCast: toUint224 overflow'
    );
    return b;
  }

  /**
   * @dev Downcast to a uint240, reverting on overflow.
   */
  function toUint240(
    uint256 a
  )
    internal
    pure
    returns (uint240)
  {
    uint240 b = uint240(a);
    require(
      uint256(b) == a,
      'SafeCast: toUint240 overflow'
    );
    return b;
  }
}

File 22 of 38 : SM1Rewards.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeERC20 } from '../../../dependencies/open-zeppelin/SafeERC20.sol';
import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { IERC20 } from '../../../interfaces/IERC20.sol';
import { Math } from '../../../utils/Math.sol';
import { SafeCast } from '../lib/SafeCast.sol';
import { SM1EpochSchedule } from './SM1EpochSchedule.sol';

/**
 * @title SM1Rewards
 * @author dYdX
 *
 * @dev Manages the distribution of token rewards.
 *
 *  Rewards are distributed continuously. After each second, an account earns rewards `r` according
 *  to the following formula:
 *
 *      r = R * s / S
 *
 *  Where:
 *    - `R` is the rewards distributed globally each second, also called the “emission rate.”
 *    - `s` is the account's staked balance in that second (technically, it is measured at the
 *      end of the second)
 *    - `S` is the sum total of all staked balances in that second (again, measured at the end of
 *      the second)
 *
 *  The parameter `R` can be configured by the contract owner. For every second that elapses,
 *  exactly `R` tokens will accrue to users, save for rounding errors, and with the exception that
 *  while the total staked balance is zero, no tokens will accrue to anyone.
 *
 *  The accounting works as follows: A global index is stored which represents the cumulative
 *  number of rewards tokens earned per staked token since the start of the distribution.
 *  The value of this index increases over time, and there are two factors affecting the rate of
 *  increase:
 *    1) The emission rate (in the numerator)
 *    2) The total number of staked tokens (in the denominator)
 *
 *  Whenever either factor changes, in some timestamp T, we settle the global index up to T by
 *  calculating the increase in the index since the last update using the OLD values of the factors:
 *
 *    indexDelta = timeDelta * emissionPerSecond * INDEX_BASE / totalStaked
 *
 *  Where `INDEX_BASE` is a scaling factor used to allow more precision in the storage of the index.
 *
 *  For each user we store an accrued rewards balance, as well as a user index, which is a cache of
 *  the global index at the time that the user's accrued rewards balance was last updated. Then at
 *  any point in time, a user's claimable rewards are represented by the following:
 *
 *    rewards = _USER_REWARDS_BALANCES_[user] + userStaked * (
 *                settledGlobalIndex - _USER_INDEXES_[user]
 *              ) / INDEX_BASE
 */
abstract contract SM1Rewards is
  SM1EpochSchedule
{
  using SafeCast for uint256;
  using SafeERC20 for IERC20;
  using SafeMath for uint256;

  // ============ Constants ============

  /// @dev Additional precision used to represent the global and user index values.
  uint256 private constant INDEX_BASE = 10**18;

  /// @notice The rewards token.
  IERC20 public immutable REWARDS_TOKEN;

  /// @notice Address to pull rewards from. Must have provided an allowance to this contract.
  address public immutable REWARDS_TREASURY;

  /// @notice Start timestamp (inclusive) of the period in which rewards can be earned.
  uint256 public immutable DISTRIBUTION_START;

  /// @notice End timestamp (exclusive) of the period in which rewards can be earned.
  uint256 public immutable DISTRIBUTION_END;

  // ============ Events ============

  event RewardsPerSecondUpdated(
    uint256 emissionPerSecond
  );

  event GlobalIndexUpdated(
    uint256 index
  );

  event UserIndexUpdated(
    address indexed user,
    uint256 index,
    uint256 unclaimedRewards
  );

  event ClaimedRewards(
    address indexed user,
    address recipient,
    uint256 claimedRewards
  );

  // ============ Constructor ============

  constructor(
    IERC20 rewardsToken,
    address rewardsTreasury,
    uint256 distributionStart,
    uint256 distributionEnd
  ) {
    require(
      distributionEnd >= distributionStart,
      'SM1Rewards: Invalid parameters'
    );
    REWARDS_TOKEN = rewardsToken;
    REWARDS_TREASURY = rewardsTreasury;
    DISTRIBUTION_START = distributionStart;
    DISTRIBUTION_END = distributionEnd;
  }

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

  /**
   * @notice The current emission rate of rewards.
   *
   * @return The number of rewards tokens issued globally each second.
   */
  function getRewardsPerSecond()
    external
    view
    returns (uint256)
  {
    return _REWARDS_PER_SECOND_;
  }

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

  /**
   * @dev Initialize the contract.
   */
  function __SM1Rewards_init()
    internal
  {
    _GLOBAL_INDEX_TIMESTAMP_ = Math.max(block.timestamp, DISTRIBUTION_START).toUint32();
  }

  /**
   * @dev Set the emission rate of rewards.
   *
   *  IMPORTANT: Do not call this function without settling the total staked balance first, to
   *  ensure that the index is settled up to the epoch boundaries.
   *
   * @param  emissionPerSecond  The new number of rewards tokens to give out each second.
   * @param  totalStaked        The total staked balance.
   */
  function _setRewardsPerSecond(
    uint256 emissionPerSecond,
    uint256 totalStaked
  )
    internal
  {
    _settleGlobalIndexUpToNow(totalStaked);
    _REWARDS_PER_SECOND_ = emissionPerSecond;
    emit RewardsPerSecondUpdated(emissionPerSecond);
  }

  /**
   * @dev Claim tokens, sending them to the specified recipient.
   *
   *  Note: In order to claim all accrued rewards, the total and user staked balances must first be
   *  settled before calling this function.
   *
   * @param  user       The user's address.
   * @param  recipient  The address to send rewards to.
   *
   * @return The number of rewards tokens claimed.
   */
  function _claimRewards(
    address user,
    address recipient
  )
    internal
    returns (uint256)
  {
    uint256 accruedRewards = _USER_REWARDS_BALANCES_[user];
    _USER_REWARDS_BALANCES_[user] = 0;
    REWARDS_TOKEN.safeTransferFrom(REWARDS_TREASURY, recipient, accruedRewards);
    emit ClaimedRewards(user, recipient, accruedRewards);
    return accruedRewards;
  }

  /**
   * @dev Settle a user's rewards up to the latest global index as of `block.timestamp`. Triggers a
   *  settlement of the global index up to `block.timestamp`. Should be called with the OLD user
   *  and total balances.
   *
   * @param  user         The user's address.
   * @param  userStaked   Tokens staked by the user during the period since the last user index
   *                      update.
   * @param  totalStaked  Total tokens staked by all users during the period since the last global
   *                      index update.
   *
   * @return The user's accrued rewards, including past unclaimed rewards.
   */
  function _settleUserRewardsUpToNow(
    address user,
    uint256 userStaked,
    uint256 totalStaked
  )
    internal
    returns (uint256)
  {
    uint256 globalIndex = _settleGlobalIndexUpToNow(totalStaked);
    return _settleUserRewardsUpToIndex(user, userStaked, globalIndex);
  }

  /**
   * @dev Settle a user's rewards up to an epoch boundary. Should be used to partially settle a
   *  user's rewards if their balance was known to have changed on that epoch boundary.
   *
   * @param  user         The user's address.
   * @param  userStaked   Tokens staked by the user. Should be accurate for the time period
   *                      since the last update to this user and up to the end of the
   *                      specified epoch.
   * @param  epochNumber  Settle the user's rewards up to the end of this epoch.
   *
   * @return The user's accrued rewards, including past unclaimed rewards, up to the end of the
   *  specified epoch.
   */
  function _settleUserRewardsUpToEpoch(
    address user,
    uint256 userStaked,
    uint256 epochNumber
  )
    internal
    returns (uint256)
  {
    uint256 globalIndex = _EPOCH_INDEXES_[epochNumber];
    return _settleUserRewardsUpToIndex(user, userStaked, globalIndex);
  }

  /**
   * @dev Settle the global index up to the end of the given epoch.
   *
   *  IMPORTANT: This function should only be called under conditions which ensure the following:
   *    - `epochNumber` < the current epoch number
   *    - `_GLOBAL_INDEX_TIMESTAMP_ < settleUpToTimestamp`
   *    - `_EPOCH_INDEXES_[epochNumber] = 0`
   */
  function _settleGlobalIndexUpToEpoch(
    uint256 totalStaked,
    uint256 epochNumber
  )
    internal
    returns (uint256)
  {
    uint256 settleUpToTimestamp = getStartOfEpoch(epochNumber.add(1));

    uint256 globalIndex = _settleGlobalIndexUpToTimestamp(totalStaked, settleUpToTimestamp);
    _EPOCH_INDEXES_[epochNumber] = globalIndex;
    return globalIndex;
  }

  // ============ Private Functions ============

  /**
   * @dev Updates the global index, reflecting cumulative rewards given out per staked token.
   *
   * @param  totalStaked          The total staked balance, which should be constant in the interval
   *                              since the last update to the global index.
   *
   * @return The new global index.
   */
  function _settleGlobalIndexUpToNow(
    uint256 totalStaked
  )
    private
    returns (uint256)
  {
    return _settleGlobalIndexUpToTimestamp(totalStaked, block.timestamp);
  }

  /**
   * @dev Helper function which settles a user's rewards up to a global index. Should be called
   *  any time a user's staked balance changes, with the OLD user and total balances.
   *
   * @param  user            The user's address.
   * @param  userStaked      Tokens staked by the user during the period since the last user index
   *                         update.
   * @param  newGlobalIndex  The new index value to bring the user index up to. MUST NOT be less
   *                         than the user's index.
   *
   * @return The user's accrued rewards, including past unclaimed rewards.
   */
  function _settleUserRewardsUpToIndex(
    address user,
    uint256 userStaked,
    uint256 newGlobalIndex
  )
    private
    returns (uint256)
  {
    uint256 oldAccruedRewards = _USER_REWARDS_BALANCES_[user];
    uint256 oldUserIndex = _USER_INDEXES_[user];

    if (oldUserIndex == newGlobalIndex) {
      return oldAccruedRewards;
    }

    uint256 newAccruedRewards;
    if (userStaked == 0) {
      // Note: Even if the user's staked balance is zero, we still need to update the user index.
      newAccruedRewards = oldAccruedRewards;
    } else {
      // Calculate newly accrued rewards since the last update to the user's index.
      uint256 indexDelta = newGlobalIndex.sub(oldUserIndex);
      uint256 accruedRewardsDelta = userStaked.mul(indexDelta).div(INDEX_BASE);
      newAccruedRewards = oldAccruedRewards.add(accruedRewardsDelta);

      // Update the user's rewards.
      _USER_REWARDS_BALANCES_[user] = newAccruedRewards;
    }

    // Update the user's index.
    _USER_INDEXES_[user] = newGlobalIndex;
    emit UserIndexUpdated(user, newGlobalIndex, newAccruedRewards);
    return newAccruedRewards;
  }

  /**
   * @dev Updates the global index, reflecting cumulative rewards given out per staked token.
   *
   * @param  totalStaked          The total staked balance, which should be constant in the interval
   *                              (_GLOBAL_INDEX_TIMESTAMP_, settleUpToTimestamp).
   * @param  settleUpToTimestamp  The timestamp up to which to settle rewards. It MUST satisfy
   *                              `settleUpToTimestamp <= block.timestamp`.
   *
   * @return The new global index.
   */
  function _settleGlobalIndexUpToTimestamp(
    uint256 totalStaked,
    uint256 settleUpToTimestamp
  )
    private
    returns (uint256)
  {
    uint256 oldGlobalIndex = uint256(_GLOBAL_INDEX_);

    // The goal of this function is to calculate rewards earned since the last global index update.
    // These rewards are earned over the time interval which is the intersection of the intervals
    // [_GLOBAL_INDEX_TIMESTAMP_, settleUpToTimestamp] and [DISTRIBUTION_START, DISTRIBUTION_END].
    //
    // We can simplify a bit based on the assumption:
    //   `_GLOBAL_INDEX_TIMESTAMP_ >= DISTRIBUTION_START`
    //
    // Get the start and end of the time interval under consideration.
    uint256 intervalStart = uint256(_GLOBAL_INDEX_TIMESTAMP_);
    uint256 intervalEnd = Math.min(settleUpToTimestamp, DISTRIBUTION_END);

    // Return early if the interval has length zero (incl. case where intervalEnd < intervalStart).
    if (intervalEnd <= intervalStart) {
      return oldGlobalIndex;
    }

    // Note: If we reach this point, we must update _GLOBAL_INDEX_TIMESTAMP_.

    uint256 emissionPerSecond = _REWARDS_PER_SECOND_;

    if (emissionPerSecond == 0 || totalStaked == 0) {
      // Ensure a log is emitted if the timestamp changed, even if the index does not change.
      _GLOBAL_INDEX_TIMESTAMP_ = intervalEnd.toUint32();
      emit GlobalIndexUpdated(oldGlobalIndex);
      return oldGlobalIndex;
    }

    // Calculate the change in index over the interval.
    uint256 timeDelta = intervalEnd.sub(intervalStart);
    uint256 indexDelta = timeDelta.mul(emissionPerSecond).mul(INDEX_BASE).div(totalStaked);

    // Calculate, update, and return the new global index.
    uint256 newGlobalIndex = oldGlobalIndex.add(indexDelta);

    // Update storage. (Shared storage slot.)
    _GLOBAL_INDEX_TIMESTAMP_ = intervalEnd.toUint32();
    _GLOBAL_INDEX_ = newGlobalIndex.toUint224();

    emit GlobalIndexUpdated(newGlobalIndex);
    return newGlobalIndex;
  }
}

File 23 of 38 : SafeERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

import { IERC20 } from '../../interfaces/IERC20.sol';
import { SafeMath } from './SafeMath.sol';
import { Address } from './Address.sol';

/**
 * @title SafeERC20
 * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
 * 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 SafeMath for uint256;
  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));
  }

  function safeApprove(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    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 callOptionalReturn(IERC20 token, bytes memory data) private {
    require(address(token).isContract(), 'SafeERC20: call to non-contract');

    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = address(token).call(data);
    require(success, 'SafeERC20: low-level call failed');

    if (returndata.length > 0) {
      // Return data is optional
      // solhint-disable-next-line max-line-length
      require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed');
    }
  }
}

File 24 of 38 : SM1EpochSchedule.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { SafeCast } from '../lib/SafeCast.sol';
import { SM1Types } from '../lib/SM1Types.sol';
import { SM1Storage } from './SM1Storage.sol';

/**
 * @title SM1EpochSchedule
 * @author dYdX
 *
 * @dev Defines a function from block timestamp to epoch number.
 *
 *  The formula used is `n = floor((t - b) / a)` where:
 *    - `n` is the epoch number
 *    - `t` is the timestamp (in seconds)
 *    - `b` is a non-negative offset, indicating the start of epoch zero (in seconds)
 *    - `a` is the length of an epoch, a.k.a. the interval (in seconds)
 *
 *  Note that by restricting `b` to be non-negative, we limit ourselves to functions in which epoch
 *  zero starts at a non-negative timestamp.
 *
 *  The recommended epoch length and blackout window are 28 and 7 days respectively; however, these
 *  are modifiable by the admin, within the specified bounds.
 */
abstract contract SM1EpochSchedule is
  SM1Storage
{
  using SafeCast for uint256;
  using SafeMath for uint256;

  // ============ Events ============

  event EpochParametersChanged(
    SM1Types.EpochParameters epochParameters
  );

  event BlackoutWindowChanged(
    uint256 blackoutWindow
  );

  // ============ Initializer ============

  function __SM1EpochSchedule_init(
    uint256 interval,
    uint256 offset,
    uint256 blackoutWindow
  )
    internal
  {
    require(
      block.timestamp < offset,
      'SM1EpochSchedule: Epoch zero must start after initialization'
    );
    _setBlackoutWindow(blackoutWindow);
    _setEpochParameters(interval, offset);
  }

  // ============ Public Functions ============

  /**
   * @notice Get the epoch at the current block timestamp.
   *
   *  NOTE: Reverts if epoch zero has not started.
   *
   * @return The current epoch number.
   */
  function getCurrentEpoch()
    public
    view
    returns (uint256)
  {
    (uint256 interval, uint256 offsetTimestamp) = _getIntervalAndOffsetTimestamp();
    return offsetTimestamp.div(interval);
  }

  /**
   * @notice Get the time remaining in the current epoch.
   *
   *  NOTE: Reverts if epoch zero has not started.
   *
   * @return The number of seconds until the next epoch.
   */
  function getTimeRemainingInCurrentEpoch()
    public
    view
    returns (uint256)
  {
    (uint256 interval, uint256 offsetTimestamp) = _getIntervalAndOffsetTimestamp();
    uint256 timeElapsedInEpoch = offsetTimestamp.mod(interval);
    return interval.sub(timeElapsedInEpoch);
  }

  /**
   * @notice Given an epoch number, get the start of that epoch. Calculated as `t = (n * a) + b`.
   *
   * @return The timestamp in seconds representing the start of that epoch.
   */
  function getStartOfEpoch(
    uint256 epochNumber
  )
    public
    view
    returns (uint256)
  {
    SM1Types.EpochParameters memory epochParameters = _EPOCH_PARAMETERS_;
    uint256 interval = uint256(epochParameters.interval);
    uint256 offset = uint256(epochParameters.offset);
    return epochNumber.mul(interval).add(offset);
  }

  /**
   * @notice Check whether we are at or past the start of epoch zero.
   *
   * @return Boolean `true` if the current timestamp is at least the start of epoch zero,
   *  otherwise `false`.
   */
  function hasEpochZeroStarted()
    public
    view
    returns (bool)
  {
    SM1Types.EpochParameters memory epochParameters = _EPOCH_PARAMETERS_;
    uint256 offset = uint256(epochParameters.offset);
    return block.timestamp >= offset;
  }

  /**
   * @notice Check whether we are in a blackout window, where withdrawal requests are restricted.
   *  Note that before epoch zero has started, there are no blackout windows.
   *
   * @return Boolean `true` if we are in a blackout window, otherwise `false`.
   */
  function inBlackoutWindow()
    public
    view
    returns (bool)
  {
    return hasEpochZeroStarted() && getTimeRemainingInCurrentEpoch() <= _BLACKOUT_WINDOW_;
  }

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

  function _setEpochParameters(
    uint256 interval,
    uint256 offset
  )
    internal
  {
    SM1Types.EpochParameters memory epochParameters =
      SM1Types.EpochParameters({interval: interval.toUint128(), offset: offset.toUint128()});
    _EPOCH_PARAMETERS_ = epochParameters;
    emit EpochParametersChanged(epochParameters);
  }

  function _setBlackoutWindow(
    uint256 blackoutWindow
  )
    internal
  {
    _BLACKOUT_WINDOW_ = blackoutWindow;
    emit BlackoutWindowChanged(blackoutWindow);
  }

  // ============ Private Functions ============

  /**
   * @dev Helper function to read params from storage and apply offset to the given timestamp.
   *  Recall that the formula for epoch number is `n = (t - b) / a`.
   *
   *  NOTE: Reverts if epoch zero has not started.
   *
   * @return The values `a` and `(t - b)`.
   */
  function _getIntervalAndOffsetTimestamp()
    private
    view
    returns (uint256, uint256)
  {
    SM1Types.EpochParameters memory epochParameters = _EPOCH_PARAMETERS_;
    uint256 interval = uint256(epochParameters.interval);
    uint256 offset = uint256(epochParameters.offset);

    require(
      block.timestamp >= offset,
      'SM1EpochSchedule: Epoch zero has not started'
    );

    uint256 offsetTimestamp = block.timestamp.sub(offset);
    return (interval, offsetTimestamp);
  }
}

File 25 of 38 : Address.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
  /**
   * @dev Returns true if `account` is a contract.
   *
   * [IMPORTANT]
   * ====
   * It is unsafe to assume that an address for which this function returns
   * false is an externally-owned account (EOA) and not a contract.
   *
   * Among others, `isContract` will return false for the following
   * types of addresses:
   *
   *  - an externally-owned account
   *  - a contract in construction
   *  - an address where a contract will be created
   *  - an address where a contract lived, but was destroyed
   * ====
   */
  function isContract(address account) internal view returns (bool) {
    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
    // for accounts without code, i.e. `keccak256('')`
    bytes32 codehash;
    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
    // solhint-disable-next-line no-inline-assembly
    assembly {
      codehash := extcodehash(account)
    }
    return (codehash != accountHash && codehash != 0x0);
  }

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

    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
    (bool success, ) = recipient.call{value: amount}('');
    require(success, 'Address: unable to send value, recipient may have reverted');
  }
}

File 26 of 38 : SM1ERC20.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { IERC20 } from '../../../interfaces/IERC20.sol';
import { IERC20Detailed } from '../../../interfaces/IERC20Detailed.sol';
import { SM1Types } from '../lib/SM1Types.sol';
import { SM1GovernancePowerDelegation } from './SM1GovernancePowerDelegation.sol';
import { SM1StakedBalances } from './SM1StakedBalances.sol';

/**
 * @title SM1ERC20
 * @author dYdX
 *
 * @dev ERC20 interface for staked tokens. Implements governance functionality for the tokens.
 *
 *  Also allows a user with an active stake to transfer their staked tokens to another user,
 *  even if they would otherwise be restricted from withdrawing.
 */
abstract contract SM1ERC20 is
  SM1StakedBalances,
  SM1GovernancePowerDelegation,
  IERC20Detailed
{
  using SafeMath for uint256;

  // ============ Constants ============

  /// @notice EIP-712 typehash for token approval via EIP-2612 permit.
  bytes32 public constant PERMIT_TYPEHASH = keccak256(
    'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'
  );

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

  function name()
    external
    pure
    override
    returns (string memory)
  {
    return 'Staked DYDX';
  }

  function symbol()
    external
    pure
    override
    returns (string memory)
  {
    return 'stkDYDX';
  }

  function decimals()
    external
    pure
    override
    returns (uint8)
  {
    return 18;
  }

  /**
   * @notice Get the total supply of staked balances.
   *
   *  Note that due to the exchange rate, this is different than querying the total balance of
   *  underyling token staked to this contract.
   *
   * @return The sum of all staked balances.
   */
  function totalSupply()
    external
    view
    override
    returns (uint256)
  {
    return getTotalActiveBalanceCurrentEpoch() + getTotalInactiveBalanceCurrentEpoch();
  }

  /**
   * @notice Get a user's staked balance.
   *
   *  Note that due to the exchange rate, one unit of staked balance may not be equivalent to one
   *  unit of the underlying token. Also note that a user's staked balance is different from a
   *  user's transferable balance.
   *
   * @param  account  The account to get the balance of.
   *
   * @return The user's staked balance.
   */
  function balanceOf(
    address account
  )
    public
    view
    override(SM1GovernancePowerDelegation, IERC20)
    returns (uint256)
  {
    return getActiveBalanceCurrentEpoch(account) + getInactiveBalanceCurrentEpoch(account);
  }

  function transfer(
    address recipient,
    uint256 amount
  )
    external
    override
    nonReentrant
    returns (bool)
  {
    _transfer(msg.sender, recipient, amount);
    return true;
  }

  function allowance(
    address owner,
    address spender
  )
    external
    view
    override
    returns (uint256)
  {
    return _ALLOWANCES_[owner][spender];
  }

  function approve(
    address spender,
    uint256 amount
  )
    external
    override
    returns (bool)
  {
    _approve(msg.sender, spender, amount);
    return true;
  }

  function transferFrom(
    address sender,
    address recipient,
    uint256 amount
  )
    external
    override
    nonReentrant
    returns (bool)
  {
    _transfer(sender, recipient, amount);
    _approve(
      sender,
      msg.sender,
      _ALLOWANCES_[sender][msg.sender].sub(amount, 'SM1ERC20: transfer amount exceeds allowance')
    );
    return true;
  }

  function increaseAllowance(
    address spender,
    uint256 addedValue
  )
    external
    returns (bool)
  {
    _approve(msg.sender, spender, _ALLOWANCES_[msg.sender][spender].add(addedValue));
    return true;
  }

  function decreaseAllowance(
    address spender,
    uint256 subtractedValue
  )
    external
    returns (bool)
  {
    _approve(
      msg.sender,
      spender,
      _ALLOWANCES_[msg.sender][spender].sub(
        subtractedValue,
        'SM1ERC20: Decreased allowance below zero'
      )
    );
    return true;
  }

  /**
   * @notice Implements the permit function as specified in EIP-2612.
   *
   * @param  owner     Address of the token owner.
   * @param  spender   Address of the spender.
   * @param  value     Amount of allowance.
   * @param  deadline  Expiration timestamp for the signature.
   * @param  v         Signature param.
   * @param  r         Signature param.
   * @param  s         Signature param.
   */
  function permit(
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  )
    external
  {
    require(
      owner != address(0),
      'SM1ERC20: INVALID_OWNER'
    );
    require(
      block.timestamp <= deadline,
      'SM1ERC20: INVALID_EXPIRATION'
    );
    uint256 currentValidNonce = _NONCES_[owner];
    bytes32 digest = keccak256(
      abi.encodePacked(
        '\x19\x01',
        _DOMAIN_SEPARATOR_,
        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
      )
    );
    require(
      owner == ecrecover(digest, v, r, s),
      'SM1ERC20: INVALID_SIGNATURE'
    );
    _NONCES_[owner] = currentValidNonce.add(1);
    _approve(owner, spender, value);
  }

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

  function _transfer(
    address sender,
    address recipient,
    uint256 amount
  )
    internal
  {
    require(
      sender != address(0),
      'SM1ERC20: Transfer from address(0)'
    );
    require(
      recipient != address(0),
      'SM1ERC20: Transfer to address(0)'
    );
    require(
      getTransferableBalance(sender) >= amount,
      'SM1ERC20: Transfer exceeds next epoch active balance'
    );

    // Update staked balances and delegate snapshots.
    _transferCurrentAndNextActiveBalance(sender, recipient, amount);
    _moveDelegatesForTransfer(sender, recipient, amount);

    emit Transfer(sender, recipient, amount);
  }

  function _approve(
    address owner,
    address spender,
    uint256 amount
  )
    internal
  {
    require(
      owner != address(0),
      'SM1ERC20: Approve from address(0)'
    );
    require(
      spender != address(0),
      'SM1ERC20: Approve to address(0)'
    );

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

File 27 of 38 : IERC20Detailed.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;

import { IERC20 } from './IERC20.sol';

/**
 * @dev Interface for ERC20 including metadata
 **/
interface IERC20Detailed is IERC20 {
  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function decimals() external view returns (uint8);
}

File 28 of 38 : SM1GovernancePowerDelegation.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import {
  IGovernancePowerDelegationERC20
} from '../../../interfaces/IGovernancePowerDelegationERC20.sol';
import { SM1Types } from '../lib/SM1Types.sol';
import { SM1ExchangeRate } from './SM1ExchangeRate.sol';
import { SM1Storage } from './SM1Storage.sol';

/**
 * @title SM1GovernancePowerDelegation
 * @author dYdX
 *
 * @dev Provides support for two types of governance powers which are separately delegatable.
 *  Provides functions for delegation and for querying a user's power at a certain block number.
 *
 *  Internally, makes use of staked balances denoted in staked units, but returns underlying token
 *  units from the getPowerAtBlock() and getPowerCurrent() functions.
 *
 *  This is based on, and is designed to match, Aave's implementation, which is used in their
 *  governance token and staked token contracts.
 */
abstract contract SM1GovernancePowerDelegation is
  SM1ExchangeRate,
  IGovernancePowerDelegationERC20
{
  using SafeMath for uint256;

  // ============ Constants ============

  /// @notice EIP-712 typehash for delegation by signature of a specific governance power type.
  bytes32 public constant DELEGATE_BY_TYPE_TYPEHASH = keccak256(
    'DelegateByType(address delegatee,uint256 type,uint256 nonce,uint256 expiry)'
  );

  /// @notice EIP-712 typehash for delegation by signature of all governance powers.
  bytes32 public constant DELEGATE_TYPEHASH = keccak256(
    'Delegate(address delegatee,uint256 nonce,uint256 expiry)'
  );

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

  /**
   * @notice Delegates a specific governance power of the sender to a delegatee.
   *
   * @param  delegatee       The address to delegate power to.
   * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
   */
  function delegateByType(
    address delegatee,
    DelegationType delegationType
  )
    external
    override
  {
    _delegateByType(msg.sender, delegatee, delegationType);
  }

  /**
   * @notice Delegates all governance powers of the sender to a delegatee.
   *
   * @param  delegatee  The address to delegate power to.
   */
  function delegate(
    address delegatee
  )
    external
    override
  {
    _delegateByType(msg.sender, delegatee, DelegationType.VOTING_POWER);
    _delegateByType(msg.sender, delegatee, DelegationType.PROPOSITION_POWER);
  }

  /**
   * @dev Delegates specific governance power from signer to `delegatee` using an EIP-712 signature.
   *
   * @param  delegatee       The address to delegate votes to.
   * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
   * @param  nonce           The signer's nonce for EIP-712 signatures on this contract.
   * @param  expiry          Expiration timestamp for the signature.
   * @param  v               Signature param.
   * @param  r               Signature param.
   * @param  s               Signature param.
   */
  function delegateByTypeBySig(
    address delegatee,
    DelegationType delegationType,
    uint256 nonce,
    uint256 expiry,
    uint8 v,
    bytes32 r,
    bytes32 s
  )
    external
  {
    bytes32 structHash = keccak256(
      abi.encode(DELEGATE_BY_TYPE_TYPEHASH, delegatee, uint256(delegationType), nonce, expiry)
    );
    bytes32 digest = keccak256(abi.encodePacked('\x19\x01', _DOMAIN_SEPARATOR_, structHash));
    address signer = ecrecover(digest, v, r, s);
    require(
      signer != address(0),
      'SM1GovernancePowerDelegation: INVALID_SIGNATURE'
    );
    require(
      nonce == _NONCES_[signer]++,
      'SM1GovernancePowerDelegation: INVALID_NONCE'
    );
    require(
      block.timestamp <= expiry,
      'SM1GovernancePowerDelegation: INVALID_EXPIRATION'
    );
    _delegateByType(signer, delegatee, delegationType);
  }

  /**
   * @dev Delegates both governance powers from signer to `delegatee` using an EIP-712 signature.
   *
   * @param  delegatee  The address to delegate votes to.
   * @param  nonce      The signer's nonce for EIP-712 signatures on this contract.
   * @param  expiry     Expiration timestamp for the signature.
   * @param  v          Signature param.
   * @param  r          Signature param.
   * @param  s          Signature param.
   */
  function delegateBySig(
    address delegatee,
    uint256 nonce,
    uint256 expiry,
    uint8 v,
    bytes32 r,
    bytes32 s
  )
    external
  {
    bytes32 structHash = keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee, nonce, expiry));
    bytes32 digest = keccak256(abi.encodePacked('\x19\x01', _DOMAIN_SEPARATOR_, structHash));
    address signer = ecrecover(digest, v, r, s);
    require(
      signer != address(0),
      'SM1GovernancePowerDelegation: INVALID_SIGNATURE'
    );
    require(
      nonce == _NONCES_[signer]++,
      'SM1GovernancePowerDelegation: INVALID_NONCE'
    );
    require(
      block.timestamp <= expiry,
      'SM1GovernancePowerDelegation: INVALID_EXPIRATION'
    );
    _delegateByType(signer, delegatee, DelegationType.VOTING_POWER);
    _delegateByType(signer, delegatee, DelegationType.PROPOSITION_POWER);
  }

  /**
   * @notice Returns the delegatee of a user.
   *
   * @param  delegator       The address of the delegator.
   * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
   */
  function getDelegateeByType(
    address delegator,
    DelegationType delegationType
  )
    external
    override
    view
    returns (address)
  {
    (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);

    return _getDelegatee(delegator, delegates);
  }

  /**
   * @notice Returns the current power of a user. The current power is the power delegated
   *  at the time of the last snapshot.
   *
   * @param  user            The user whose power to query.
   * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
   */
  function getPowerCurrent(
    address user,
    DelegationType delegationType
  )
    external
    override
    view
    returns (uint256)
  {
    return getPowerAtBlock(user, block.number, delegationType);
  }

  /**
   * @notice Get the next valid nonce for EIP-712 signatures.
   *
   *  This nonce should be used when signing for any of the following functions:
   *   - permit()
   *   - delegateByTypeBySig()
   *   - delegateBySig()
   */
  function nonces(
    address owner
  )
    external
    view
    returns (uint256)
  {
    return _NONCES_[owner];
  }

  // ============ Public Functions ============

  function balanceOf(
    address account
  )
    public
    view
    virtual
    returns (uint256);

  /**
   * @notice Returns the power of a user at a certain block, denominated in underlying token units.
   *
   * @param  user            The user whose power to query.
   * @param  blockNumber     The block number at which to get the user's power.
   * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
   *
   * @return The user's governance power of the specified type, in underlying token units.
   */
  function getPowerAtBlock(
    address user,
    uint256 blockNumber,
    DelegationType delegationType
  )
    public
    override
    view
    returns (uint256)
  {
    (
      mapping(address => mapping(uint256 => SM1Types.Snapshot)) storage snapshots,
      mapping(address => uint256) storage snapshotCounts,
      // unused: delegates
    ) = _getDelegationDataByType(delegationType);

    uint256 stakeAmount = _findValueAtBlock(
      snapshots[user],
      snapshotCounts[user],
      blockNumber,
      0
    );
    uint256 exchangeRate = _findValueAtBlock(
      _EXCHANGE_RATE_SNAPSHOTS_,
      _EXCHANGE_RATE_SNAPSHOT_COUNT_,
      blockNumber,
      EXCHANGE_RATE_BASE
    );
    return underlyingAmountFromStakeAmountWithExchangeRate(stakeAmount, exchangeRate);
  }

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

  /**
   * @dev Delegates one specific power to a delegatee.
   *
   * @param  delegator       The user whose power to delegate.
   * @param  delegatee       The address to delegate power to.
   * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
   */
  function _delegateByType(
    address delegator,
    address delegatee,
    DelegationType delegationType
  )
    internal
  {
    require(
      delegatee != address(0),
      'SM1GovernancePowerDelegation: INVALID_DELEGATEE'
    );

    (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);
    uint256 delegatorBalance = balanceOf(delegator);
    address previousDelegatee = _getDelegatee(delegator, delegates);

    delegates[delegator] = delegatee;

    _moveDelegatesByType(previousDelegatee, delegatee, delegatorBalance, delegationType);
    emit DelegateChanged(delegator, delegatee, delegationType);
  }

  /**
   * @dev Update delegate snapshots whenever staked tokens are transfered, minted, or burned.
   *
   * @param  from          The sender.
   * @param  to            The recipient.
   * @param  stakedAmount  The amount being transfered, denominated in staked units.
   */
  function _moveDelegatesForTransfer(
    address from,
    address to,
    uint256 stakedAmount
  )
    internal
  {
    address votingPowerFromDelegatee = _getDelegatee(from, _VOTING_DELEGATES_);
    address votingPowerToDelegatee = _getDelegatee(to, _VOTING_DELEGATES_);

    _moveDelegatesByType(
      votingPowerFromDelegatee,
      votingPowerToDelegatee,
      stakedAmount,
      DelegationType.VOTING_POWER
    );

    address propositionPowerFromDelegatee = _getDelegatee(from, _PROPOSITION_DELEGATES_);
    address propositionPowerToDelegatee = _getDelegatee(to, _PROPOSITION_DELEGATES_);

    _moveDelegatesByType(
      propositionPowerFromDelegatee,
      propositionPowerToDelegatee,
      stakedAmount,
      DelegationType.PROPOSITION_POWER
    );
  }

  /**
   * @dev Moves power from one user to another.
   *
   * @param  from            The user from which delegated power is moved.
   * @param  to              The user that will receive the delegated power.
   * @param  amount          The amount of power to be moved.
   * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
   */
  function _moveDelegatesByType(
    address from,
    address to,
    uint256 amount,
    DelegationType delegationType
  )
    internal
  {
    if (from == to) {
      return;
    }

    (
      mapping(address => mapping(uint256 => SM1Types.Snapshot)) storage snapshots,
      mapping(address => uint256) storage snapshotCounts,
      // unused: delegates
    ) = _getDelegationDataByType(delegationType);

    if (from != address(0)) {
      mapping(uint256 => SM1Types.Snapshot) storage fromSnapshots = snapshots[from];
      uint256 fromSnapshotCount = snapshotCounts[from];
      uint256 previousBalance = 0;

      if (fromSnapshotCount != 0) {
        previousBalance = fromSnapshots[fromSnapshotCount - 1].value;
      }

      uint256 newBalance = previousBalance.sub(amount);
      snapshotCounts[from] = _writeSnapshot(
        fromSnapshots,
        fromSnapshotCount,
        newBalance
      );

      emit DelegatedPowerChanged(from, newBalance, delegationType);
    }

    if (to != address(0)) {
      mapping(uint256 => SM1Types.Snapshot) storage toSnapshots = snapshots[to];
      uint256 toSnapshotCount = snapshotCounts[to];
      uint256 previousBalance = 0;

      if (toSnapshotCount != 0) {
        previousBalance = toSnapshots[toSnapshotCount - 1].value;
      }

      uint256 newBalance = previousBalance.add(amount);
      snapshotCounts[to] = _writeSnapshot(
        toSnapshots,
        toSnapshotCount,
        newBalance
      );

      emit DelegatedPowerChanged(to, newBalance, delegationType);
    }
  }

  /**
   * @dev Returns delegation data (snapshot, snapshotCount, delegates) by delegation type.
   *
   * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
   *
   * @return The mapping of each user to a mapping of snapshots.
   * @return The mapping of each user to the total number of snapshots for that user.
   * @return The mapping of each user to the user's delegate.
   */
  function _getDelegationDataByType(
    DelegationType delegationType
  )
    internal
    view
    returns (
      mapping(address => mapping(uint256 => SM1Types.Snapshot)) storage,
      mapping(address => uint256) storage,
      mapping(address => address) storage
    )
  {
    if (delegationType == DelegationType.VOTING_POWER) {
      return (
        _VOTING_SNAPSHOTS_,
        _VOTING_SNAPSHOT_COUNTS_,
        _VOTING_DELEGATES_
      );
    } else {
      return (
        _PROPOSITION_SNAPSHOTS_,
        _PROPOSITION_SNAPSHOT_COUNTS_,
        _PROPOSITION_DELEGATES_
      );
    }
  }

  /**
   * @dev Returns the delegatee of a user. If a user never performed any delegation, their
   *  delegated address will be 0x0, in which case we return the user's own address.
   *
   * @param  delegator  The address of the user for which return the delegatee.
   * @param  delegates  The mapping of delegates for a particular type of delegation.
   */
  function _getDelegatee(
    address delegator,
    mapping(address => address) storage delegates
  )
    internal
    view
    returns (address)
  {
    address previousDelegatee = delegates[delegator];

    if (previousDelegatee == address(0)) {
      return delegator;
    }

    return previousDelegatee;
  }
}

File 29 of 38 : IGovernancePowerDelegationERC20.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;

interface IGovernancePowerDelegationERC20 {

  enum DelegationType {
    VOTING_POWER,
    PROPOSITION_POWER
  }

  /**
   * @dev Emitted when a user delegates governance power to another user.
   *
   * @param  delegator       The delegator.
   * @param  delegatee       The delegatee.
   * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
   */
  event DelegateChanged(
    address indexed delegator,
    address indexed delegatee,
    DelegationType delegationType
  );

  /**
   * @dev Emitted when an action changes the delegated power of a user.
   *
   * @param  user            The user whose delegated power has changed.
   * @param  amount          The new amount of delegated power for the user.
   * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
   */
  event DelegatedPowerChanged(address indexed user, uint256 amount, DelegationType delegationType);

  /**
   * @dev Delegates a specific governance power to a delegatee.
   *
   * @param  delegatee       The address to delegate power to.
   * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
   */
  function delegateByType(address delegatee, DelegationType delegationType) external virtual;

  /**
   * @dev Delegates all governance powers to a delegatee.
   *
   * @param  delegatee  The user to which the power will be delegated.
   */
  function delegate(address delegatee) external virtual;

  /**
   * @dev Returns the delegatee of an user.
   *
   * @param  delegator       The address of the delegator.
   * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
   */
  function getDelegateeByType(address delegator, DelegationType delegationType)
    external
    view
    virtual
    returns (address);

  /**
   * @dev Returns the current delegated power of a user. The current power is the power delegated
   *  at the time of the last snapshot.
   *
   * @param  user            The user whose power to query.
   * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
   */
  function getPowerCurrent(address user, DelegationType delegationType)
    external
    view
    virtual
    returns (uint256);

  /**
   * @dev Returns the delegated power of a user at a certain block.
   *
   * @param  user            The user whose power to query.
   * @param  blockNumber     The block number at which to get the user's power.
   * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
   */
  function getPowerAtBlock(
    address user,
    uint256 blockNumber,
    DelegationType delegationType
  )
    external
    view
    virtual
    returns (uint256);
}

File 30 of 38 : SM1ExchangeRate.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol';
import { SM1Snapshots } from './SM1Snapshots.sol';
import { SM1Storage } from './SM1Storage.sol';

/**
 * @title SM1ExchangeRate
 * @author dYdX
 *
 * @dev Performs math using the exchange rate, which converts between underlying units of the token
 *  that was staked (e.g. STAKED_TOKEN.balanceOf(account)), and staked units, used by this contract
 *  for all staked balances (e.g. this.balanceOf(account)).
 *
 *  OVERVIEW:
 *
 *   The exchange rate is stored as a multiple of EXCHANGE_RATE_BASE, and represents the number of
 *   staked balance units that each unit of underlying token is worth. Before any slashes have
 *   occurred, the exchange rate is equal to one. The exchange rate can increase with each slash,
 *   indicating that staked balances are becoming less and less valuable, per unit, relative to the
 *   underlying token.
 *
 *  AVOIDING OVERFLOW AND UNDERFLOW:
 *
 *   Staked balances are represented internally as uint240, so the result of an operation returning
 *   a staked balances must return a value less than 2^240. Intermediate values in calcuations are
 *   represented as uint256, so all operations within a calculation must return values under 2^256.
 *
 *   In the functions below operating on the exchange rate, we are strategic in our choice of the
 *   order of multiplication and division operations, in order to avoid both overflow and underflow.
 *
 *   We use the following assumptions and principles to implement this module:
 *     - (ASSUMPTION) An amount denoted in underlying token units is never greater than 10^28.
 *     - If the exchange rate is greater than 10^46, then we may perform division on the exchange
 *         rate before performing multiplication, provided that the denominator is not greater
 *         than 10^28 (to ensure a result with at least 18 decimals of precision). Specifically,
 *         we use EXCHANGE_RATE_MAY_OVERFLOW as the cutoff, which is a number greater than 10^46.
 *     - Since staked balances are stored as uint240, we cap the exchange rate to ensure that a
 *         staked balance can never overflow (using the assumption above).
 */
abstract contract SM1ExchangeRate is
  SM1Snapshots,
  SM1Storage
{
  using SafeMath for uint256;

  // ============ Constants ============

  /// @notice The assumed upper bound on the total supply of the staked token.
  uint256 public constant MAX_UNDERLYING_BALANCE = 1e28;

  /// @notice Base unit used to represent the exchange rate, for additional precision.
  uint256 public constant EXCHANGE_RATE_BASE = 1e18;

  /// @notice Cutoff where an exchange rate may overflow after multiplying by an underlying balance.
  /// @dev Approximately 1.2e49
  uint256 public constant EXCHANGE_RATE_MAY_OVERFLOW = (2 ** 256 - 1) / MAX_UNDERLYING_BALANCE;

  /// @notice Cutoff where a stake amount may overflow after multiplying by EXCHANGE_RATE_BASE.
  /// @dev Approximately 1.2e59
  uint256 public constant STAKE_AMOUNT_MAY_OVERFLOW = (2 ** 256 - 1) / EXCHANGE_RATE_BASE;

  /// @notice Max exchange rate.
  /// @dev Approximately 1.8e62
  uint256 public constant MAX_EXCHANGE_RATE = (
    ((2 ** 240 - 1) / MAX_UNDERLYING_BALANCE) * EXCHANGE_RATE_BASE
  );

  // ============ Initializer ============

  function __SM1ExchangeRate_init()
    internal
  {
    _EXCHANGE_RATE_ = EXCHANGE_RATE_BASE;
  }

  function stakeAmountFromUnderlyingAmount(
    uint256 underlyingAmount
  )
    internal
    view
    returns (uint256)
  {
    uint256 exchangeRate = _EXCHANGE_RATE_;

    if (exchangeRate > EXCHANGE_RATE_MAY_OVERFLOW) {
      uint256 exchangeRateUnbased = exchangeRate.div(EXCHANGE_RATE_BASE);
      return underlyingAmount.mul(exchangeRateUnbased);
    } else {
      return underlyingAmount.mul(exchangeRate).div(EXCHANGE_RATE_BASE);
    }
  }

  function underlyingAmountFromStakeAmount(
    uint256 stakeAmount
  )
    internal
    view
    returns (uint256)
  {
    return underlyingAmountFromStakeAmountWithExchangeRate(stakeAmount, _EXCHANGE_RATE_);
  }

  function underlyingAmountFromStakeAmountWithExchangeRate(
    uint256 stakeAmount,
    uint256 exchangeRate
  )
    internal
    pure
    returns (uint256)
  {
    if (stakeAmount > STAKE_AMOUNT_MAY_OVERFLOW) {
      // Note that this case implies that exchangeRate > EXCHANGE_RATE_MAY_OVERFLOW.
      uint256 exchangeRateUnbased = exchangeRate.div(EXCHANGE_RATE_BASE);
      return stakeAmount.div(exchangeRateUnbased);
    } else {
      return stakeAmount.mul(EXCHANGE_RATE_BASE).div(exchangeRate);
    }
  }

  function updateExchangeRate(
    uint256 numerator,
    uint256 denominator
  )
    internal
    returns (uint256)
  {
    uint256 oldExchangeRate = _EXCHANGE_RATE_;

    // Avoid overflow.
    // Note that the numerator and denominator are both denominated in underlying token units.
    uint256 newExchangeRate;
    if (oldExchangeRate > EXCHANGE_RATE_MAY_OVERFLOW) {
      newExchangeRate = oldExchangeRate.div(denominator).mul(numerator);
    } else {
      newExchangeRate = oldExchangeRate.mul(numerator).div(denominator);
    }

    require(
      newExchangeRate <= MAX_EXCHANGE_RATE,
      'SM1ExchangeRate: Max exchange rate exceeded'
    );

    _EXCHANGE_RATE_SNAPSHOT_COUNT_ = _writeSnapshot(
      _EXCHANGE_RATE_SNAPSHOTS_,
      _EXCHANGE_RATE_SNAPSHOT_COUNT_,
      newExchangeRate
    );

    _EXCHANGE_RATE_ = newExchangeRate;
    return newExchangeRate;
  }
}

File 31 of 38 : SM1Snapshots.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import { SM1Types } from '../lib/SM1Types.sol';
import { SM1Storage } from './SM1Storage.sol';

/**
 * @title SM1Snapshots
 * @author dYdX
 *
 * @dev Handles storage and retrieval of historical values by block number.
 *
 *  Note that the snapshot stored at a given block number represents the value as of the end of
 *  that block.
 */
abstract contract SM1Snapshots {

  /**
   * @dev Writes a snapshot of a value at the current block.
   *
   * @param  snapshots      Storage mapping from snapshot index to snapshot struct.
   * @param  snapshotCount  The total number of snapshots in the provided mapping.
   * @param  newValue       The new value to snapshot at the current block.
   *
   * @return The new snapshot count.
   */
  function _writeSnapshot(
    mapping(uint256 => SM1Types.Snapshot) storage snapshots,
    uint256 snapshotCount,
    uint256 newValue
  )
    internal
    returns (uint256)
  {
    uint256 currentBlock = block.number;

    if (
      snapshotCount != 0 &&
      snapshots[snapshotCount - 1].blockNumber == currentBlock
    ) {
      // If there was a previous snapshot for this block, overwrite it.
      snapshots[snapshotCount - 1].value = newValue;
      return snapshotCount;
    } else {
      snapshots[snapshotCount] = SM1Types.Snapshot(currentBlock, newValue);
      return snapshotCount + 1;
    }
  }

  /**
   * @dev Search for the snapshot value at a given block. Uses binary search.
   *
   *  Reverts if `blockNumber` is greater than the current block number.
   *
   * @param  snapshots      Storage mapping from snapshot index to snapshot struct.
   * @param  snapshotCount  The total number of snapshots in the provided mapping.
   * @param  blockNumber    The block number to search for.
   * @param  initialValue   The value to return if `blockNumber` is before the earliest snapshot.
   *
   * @return The snapshot value at the specified block number.
   */
  function _findValueAtBlock(
    mapping(uint256 => SM1Types.Snapshot) storage snapshots,
    uint256 snapshotCount,
    uint256 blockNumber,
    uint256 initialValue
  )
    internal
    view
    returns (uint256)
  {
    require(
      blockNumber <= block.number,
      'SM1Snapshots: INVALID_BLOCK_NUMBER'
    );

    if (snapshotCount == 0) {
      return initialValue;
    }

    // Check earliest snapshot.
    if (blockNumber < snapshots[0].blockNumber) {
      return initialValue;
    }

    // Check latest snapshot.
    if (blockNumber >= snapshots[snapshotCount - 1].blockNumber) {
      return snapshots[snapshotCount - 1].value;
    }

    uint256 lower = 0;
    uint256 upper = snapshotCount - 1;
    while (upper > lower) {
      uint256 center = upper - (upper - lower) / 2; // Ceil, avoiding overflow.
      SM1Types.Snapshot memory snapshot = snapshots[center];
      if (snapshot.blockNumber == blockNumber) {
        return snapshot.value;
      } else if (snapshot.blockNumber < blockNumber) {
        lower = center;
      } else {
        upper = center - 1;
      }
    }
    return snapshots[lower].value;
  }
}

File 32 of 38 : BaseUpgradeabilityProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

import './Proxy.sol';
import './Address.sol';

/**
 * @title BaseUpgradeabilityProxy
 * @dev This contract implements a proxy that allows to change the
 * implementation address to which it will delegate.
 * Such a change is called an implementation upgrade.
 */
contract BaseUpgradeabilityProxy is Proxy {
  /**
   * @dev Emitted when the implementation is upgraded.
   * @param implementation Address of the new implementation.
   */
  event Upgraded(address indexed implementation);

  /**
   * @dev Storage slot with the address of the current implementation.
   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
   * validated in the constructor.
   */
  bytes32
    internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

  /**
   * @dev Returns the current implementation.
   * @return impl Address of the current implementation
   */
  function _implementation() internal override view returns (address impl) {
    bytes32 slot = IMPLEMENTATION_SLOT;
    assembly {
      impl := sload(slot)
    }
  }

  /**
   * @dev Upgrades the proxy to a new implementation.
   * @param newImplementation Address of the new implementation.
   */
  function _upgradeTo(address newImplementation) internal {
    _setImplementation(newImplementation);
    emit Upgraded(newImplementation);
  }

  /**
   * @dev Sets the implementation address of the proxy.
   * @param newImplementation Address of the new implementation.
   */
  function _setImplementation(address newImplementation) internal {
    require(
      Address.isContract(newImplementation),
      'Cannot set a proxy implementation to a non-contract address'
    );

    bytes32 slot = IMPLEMENTATION_SLOT;

    assembly {
      sstore(slot, newImplementation)
    }
  }
}

File 33 of 38 : Proxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/**
 * @title Proxy
 * @dev Implements delegation of calls to other contracts, with proper
 * forwarding of return values and bubbling of failures.
 * It defines a fallback function that delegates all calls to the address
 * returned by the abstract _implementation() internal function.
 */
abstract contract Proxy {
  /**
   * @dev Fallback function.
   * Implemented entirely in `_fallback`.
   */
  fallback() external payable {
    _fallback();
  }

  /**
   * @return The Address of the implementation.
   */
  function _implementation() internal virtual view returns (address);

  /**
   * @dev Delegates execution to an implementation contract.
   * This is a low level function that doesn't return to its internal call site.
   * It will return to the external caller whatever the implementation returns.
   * @param implementation Address to delegate.
   */
  function _delegate(address implementation) internal {
    assembly {
      // Copy msg.data. We take full control of memory in this inline assembly
      // block because it will not return to Solidity code. We overwrite the
      // Solidity scratch pad at memory position 0.
      calldatacopy(0, 0, calldatasize())

      // Call the implementation.
      // out and outsize are 0 because we don't know the size yet.
      let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

      // Copy the returned data.
      returndatacopy(0, 0, returndatasize())

      switch result
        // delegatecall returns 0 on error.
        case 0 {
          revert(0, returndatasize())
        }
        default {
          return(0, returndatasize())
        }
    }
  }

  /**
   * @dev Function that is run as the first thing in the fallback function.
   * Can be redefined in derived contracts to add functionality.
   * Redefinitions must call super._willFallback().
   */
  function _willFallback() internal virtual {}

  /**
   * @dev fallback implementation.
   * Extracted to enable manual triggering.
   */
  function _fallback() internal {
    _willFallback();
    _delegate(_implementation());
  }
}

File 34 of 38 : InitializableUpgradeabilityProxy.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;

import './BaseUpgradeabilityProxy.sol';

/**
 * @title InitializableUpgradeabilityProxy
 * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
 * implementation and init data.
 */
contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
  /**
   * @dev Contract initializer.
   * @param _logic Address of the initial implementation.
   * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
   * It should include the signature and the parameters of the function to be called, as described in
   * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
   * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
   */
  function initialize(address _logic, bytes memory _data) public payable {
    require(_implementation() == address(0));
    assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
    _setImplementation(_logic);
    if (_data.length > 0) {
      (bool success, ) = _logic.delegatecall(_data);
      require(success);
    }
  }
}

File 35 of 38 : InitializableAdminUpgradeabilityProxy.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.7.5;

import './BaseAdminUpgradeabilityProxy.sol';
import './InitializableUpgradeabilityProxy.sol';

/**
 * @title InitializableAdminUpgradeabilityProxy
 * @dev Extends from BaseAdminUpgradeabilityProxy with an initializer for
 * initializing the implementation, admin, and init data.
 */
contract InitializableAdminUpgradeabilityProxy is
  BaseAdminUpgradeabilityProxy,
  InitializableUpgradeabilityProxy
{
  /**
   * Contract initializer.
   * @param _logic address of the initial implementation.
   * @param _admin Address of the proxy administrator.
   * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
   * It should include the signature and the parameters of the function to be called, as described in
   * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
   * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
   */
  function initialize(
    address _logic,
    address _admin,
    bytes memory _data
  ) public payable {
    require(_implementation() == address(0));
    InitializableUpgradeabilityProxy.initialize(_logic, _data);
    assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
    _setAdmin(_admin);
  }

  /**
   * @dev Only fall back when the sender is not the admin.
   */
  function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) {
    BaseAdminUpgradeabilityProxy._willFallback();
  }
}

File 36 of 38 : BaseAdminUpgradeabilityProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

import './UpgradeabilityProxy.sol';

/**
 * @title BaseAdminUpgradeabilityProxy
 * @dev This contract combines an upgradeability proxy with an authorization
 * mechanism for administrative tasks.
 * All external functions in this contract must be guarded by the
 * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
 * feature proposal that would enable this to be done automatically.
 */
contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
  /**
   * @dev Emitted when the administration has been transferred.
   * @param previousAdmin Address of the previous admin.
   * @param newAdmin Address of the new admin.
   */
  event AdminChanged(address previousAdmin, address newAdmin);

  /**
   * @dev Storage slot with the admin of the contract.
   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
   * validated in the constructor.
   */

  bytes32
    internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

  /**
   * @dev Modifier to check whether the `msg.sender` is the admin.
   * If it is, it will run the function. Otherwise, it will delegate the call
   * to the implementation.
   */
  modifier ifAdmin() {
    if (msg.sender == _admin()) {
      _;
    } else {
      _fallback();
    }
  }

  /**
   * @return The address of the proxy admin.
   */
  function admin() external ifAdmin returns (address) {
    return _admin();
  }

  /**
   * @return The address of the implementation.
   */
  function implementation() external ifAdmin returns (address) {
    return _implementation();
  }

  /**
   * @dev Changes the admin of the proxy.
   * Only the current admin can call this function.
   * @param newAdmin Address to transfer proxy administration to.
   */
  function changeAdmin(address newAdmin) external ifAdmin {
    require(newAdmin != address(0), 'Cannot change the admin of a proxy to the zero address');
    emit AdminChanged(_admin(), newAdmin);
    _setAdmin(newAdmin);
  }

  /**
   * @dev Upgrade the backing implementation of the proxy.
   * Only the admin can call this function.
   * @param newImplementation Address of the new implementation.
   */
  function upgradeTo(address newImplementation) external ifAdmin {
    _upgradeTo(newImplementation);
  }

  /**
   * @dev Upgrade the backing implementation of the proxy and call a function
   * on the new implementation.
   * This is useful to initialize the proxied contract.
   * @param newImplementation Address of the new implementation.
   * @param data Data to send as msg.data in the low level call.
   * It should include the signature and the parameters of the function to be called, as described in
   * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
   */
  function upgradeToAndCall(address newImplementation, bytes calldata data)
    external
    payable
    ifAdmin
  {
    _upgradeTo(newImplementation);
    (bool success, ) = newImplementation.delegatecall(data);
    require(success);
  }

  /**
   * @return adm The admin slot.
   */
  function _admin() internal view returns (address adm) {
    bytes32 slot = ADMIN_SLOT;
    assembly {
      adm := sload(slot)
    }
  }

  /**
   * @dev Sets the address of the proxy admin.
   * @param newAdmin Address of the new proxy admin.
   */
  function _setAdmin(address newAdmin) internal {
    bytes32 slot = ADMIN_SLOT;

    assembly {
      sstore(slot, newAdmin)
    }
  }

  /**
   * @dev Only fall back when the sender is not the admin.
   */
  function _willFallback() internal virtual override {
    require(msg.sender != _admin(), 'Cannot call fallback function from the proxy admin');
    super._willFallback();
  }
}

File 37 of 38 : UpgradeabilityProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

import './BaseUpgradeabilityProxy.sol';

/**
 * @title UpgradeabilityProxy
 * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
 * implementation and init data.
 */
contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
  /**
   * @dev Contract constructor.
   * @param _logic Address of the initial implementation.
   * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
   * It should include the signature and the parameters of the function to be called, as described in
   * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
   * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
   */
  constructor(address _logic, bytes memory _data) public payable {
    assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
    _setImplementation(_logic);
    if (_data.length > 0) {
      (bool success, ) = _logic.delegatecall(_data);
      require(success);
    }
  }
}

File 38 of 38 : AdminUpgradeabilityProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.5;

import './BaseAdminUpgradeabilityProxy.sol';

/**
 * @title AdminUpgradeabilityProxy
 * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for
 * initializing the implementation, admin, and init data.
 */
contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy {
  /**
   * Contract constructor.
   * @param _logic address of the initial implementation.
   * @param _admin Address of the proxy administrator.
   * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
   * It should include the signature and the parameters of the function to be called, as described in
   * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
   * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
   */
  constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
    assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
    _setAdmin(_admin);
  }

  /**
   * @dev Only fall back when the sender is not the admin.
   */
  function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) {
    require(msg.sender != _admin(), 'Cannot call fallback function from the proxy admin');
    super._willFallback();
  }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "istanbul",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC20","name":"stakedToken","type":"address"},{"internalType":"contract IERC20","name":"rewardsToken","type":"address"},{"internalType":"address","name":"rewardsTreasury","type":"address"},{"internalType":"uint256","name":"distributionStart","type":"uint256"},{"internalType":"uint256","name":"distributionEnd","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"blackoutWindow","type":"uint256"}],"name":"BlackoutWindowChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimedRewards","type":"uint256"}],"name":"ClaimedRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"delegatee","type":"address"},{"indexed":false,"internalType":"enum IGovernancePowerDelegationERC20.DelegationType","name":"delegationType","type":"uint8"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"enum IGovernancePowerDelegationERC20.DelegationType","name":"delegationType","type":"uint8"}],"name":"DelegatedPowerChanged","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint128","name":"interval","type":"uint128"},{"internalType":"uint128","name":"offset","type":"uint128"}],"indexed":false,"internalType":"struct SM1Types.EpochParameters","name":"epochParameters","type":"tuple"}],"name":"EpochParametersChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"GlobalIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimedRewards","type":"uint256"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorClaimedRewardsFor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorStakedFor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorWithdrawalRequestedFor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorWithdrewStakeFor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"emissionPerSecond","type":"uint256"}],"name":"RewardsPerSecondUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"newExchangeRate","type":"uint256"}],"name":"Slashed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"underlyingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unclaimedRewards","type":"uint256"}],"name":"UserIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"WithdrawalRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"underlyingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"WithdrewStake","type":"event"},{"inputs":[],"name":"CLAIM_OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELEGATE_BY_TYPE_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELEGATE_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DISTRIBUTION_END","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DISTRIBUTION_START","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_DOMAIN_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_DOMAIN_SCHEMA_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_DOMAIN_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EPOCH_PARAMETERS_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXCHANGE_RATE_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXCHANGE_RATE_MAY_OVERFLOW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_EXCHANGE_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SLASH_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SLASH_NUMERATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_UNDERLYING_BALANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDS_RATE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDS_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDS_TREASURY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SLASHER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKED_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKE_AMOUNT_MAY_OVERFLOW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKE_OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"claimRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"claimRewardsFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"enum IGovernancePowerDelegationERC20.DelegationType","name":"delegationType","type":"uint8"}],"name":"delegateByType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"enum IGovernancePowerDelegationERC20.DelegationType","name":"delegationType","type":"uint8"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateByTypeBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getActiveBalanceCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getActiveBalanceNextEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlackoutWindow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"enum IGovernancePowerDelegationERC20.DelegationType","name":"delegationType","type":"uint8"}],"name":"getDelegateeByType","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDomainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEpochParameters","outputs":[{"components":[{"internalType":"uint128","name":"interval","type":"uint128"},{"internalType":"uint128","name":"offset","type":"uint128"}],"internalType":"struct SM1Types.EpochParameters","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExchangeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getExchangeRateSnapshot","outputs":[{"components":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct SM1Types.Snapshot","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExchangeRateSnapshotCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getInactiveBalanceCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getInactiveBalanceNextEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"enum IGovernancePowerDelegationERC20.DelegationType","name":"delegationType","type":"uint8"}],"name":"getPowerAtBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"enum IGovernancePowerDelegationERC20.DelegationType","name":"delegationType","type":"uint8"}],"name":"getPowerCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardsPerSecond","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getStakeAvailableToWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epochNumber","type":"uint256"}],"name":"getStartOfEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTimeRemainingInCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalActiveBalanceCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalActiveBalanceNextEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalInactiveBalanceCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalInactiveBalanceNextEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getTransferableBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"hasEpochZeroStarted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inBlackoutWindow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"interval","type":"uint256"},{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"blackoutWindow","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"requestWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"requestWithdrawalFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blackoutWindow","type":"uint256"}],"name":"setBlackoutWindow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"interval","type":"uint256"},{"internalType":"uint256","name":"offset","type":"uint256"}],"name":"setEpochParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"emissionPerSecond","type":"uint256"}],"name":"setRewardsPerSecond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestedSlashAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"slash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"underlyingAmount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"underlyingAmount","type":"uint256"}],"name":"stakeFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawMaxStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"withdrawStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"withdrawStakeFor","outputs":[],"stateMutability":"nonpayable","type":"function"}]

61012060405260006033553480156200001757600080fd5b5060405162005354380380620053548339810160408190526200003a91620000ba565b60016032558484848484838383838383838381811015620000785760405162461bcd60e51b81526004016200006f906200011e565b60405180910390fd5b6001600160601b0319606094851b811660805292841b831660a05260c09190915260e05299901b90981661010052506200016e9b505050505050505050505050565b600080600080600060a08688031215620000d2578081fd5b8551620000df8162000155565b6020870151909550620000f28162000155565b6040870151909450620001058162000155565b6060870151608090970151959894975095949392505050565b6020808252601e908201527f534d31526577617264733a20496e76616c696420706172616d65746572730000604082015260600190565b6001600160a01b03811681146200016b57600080fd5b50565b60805160601c60a05160601c60c05160e0516101005160601c615175620001df60003980610e0f528061102d5280611126528061286b5280612d4352508061161252806140a4525080610e385280612ff0525080610bcd5280613c0f525080611b465280613bed52506151756000f3fe608060405234801561001057600080fd5b50600436106104cd5760003560e01c806380d8591111610283578063c4987fd21161015c578063d8a3f3be116100ce578063e614d0d611610092578063e614d0d61461098e578063e6aa216c14610996578063ed24911d1461099e578063ef5cfb8c146109a6578063f713d8a8146109b9578063fd070296146109cc576104cd565b8063d8a3f3be14610950578063dc937e1c14610958578063dd62ed3e1461096b578063e294121e1461097e578063e58378bb14610986576104cd565b8063cbeb09aa11610120578063cbeb09aa146108ff578063d057152c14610912578063d505accf1461091a578063d547741f1461092d578063d744bd6714610940578063d837d18714610948576104cd565b8063c4987fd2146108b4578063c5601072146108c7578063c5b39666146108cf578063c6066272146108d7578063c89b0fff146108ea576104cd565b8063a457c2d7116101f5578063b29d3dbb116101b9578063b29d3dbb1461084b578063b2f4201d1461086b578063b7b038da1461087e578063b97dd9e214610886578063c2ffbb911461088e578063c3cda520146108a1576104cd565b8063a457c2d714610802578063a694fc3a14610815578063a9059cbb14610828578063aa9fbe021461083b578063b0d266bd14610843576104cd565b806395d89b411161024757806395d89b41146107bc5780639ee679e8146107c45780639fb2907f146107d7578063a1663340146107df578063a217fddf146107f2578063a2f44db0146107fa576104cd565b806380d85911146107735780638e4c4d78146107865780638eebb0fd14610799578063919cd40f146107a157806391d14854146107a9576104cd565b806335de00ab116103b55780635095af64116103275780636461d4bf116102eb5780636461d4bf146107175780636f50458d1461072a57806370a082311461073d57806373f93fbd1461075057806376f259b2146107585780637ecebe0014610760576104cd565b80635095af64146106ce5780635c19a95c146106d65780635cc33321146106e95780635da51e1f146106f1578063639b8d6214610704576104cd565b8063408aabc711610379578063408aabc71461067d57806341cbf54a1461069057806344501f981461069857806349f9d8e5146106ab5780634b35073f146106b35780634c0bcfe5146106bb576104cd565b806335de00ab1461062957806336568abe146106315780633658aa251461064457806339509351146106575780633d82e3c11461066a576104cd565b80632495c2ab1161044e5780632f2ff15d116104125780632f2ff15d146105e15780633051f940146105f457806330adf81f146105fc578063312f6b8314610604578063313ce5671461060c57806333c56e0b14610621576104cd565b80632495c2ab146105a35780632847f6ff146105b657806329b103d8146105be5780632c73c1a1146105c65780632ee40908146105ce576104cd565b8063125f844011610495578063125f84401461054d57806318160ddd146105605780631c89553c1461056857806323b872dd1461057d578063248a9ca314610590576104cd565b806301ffc9a7146104d257806303f637f6146104fb57806306ae63ae1461051057806306fdde0314610525578063095ea7b31461053a575b600080fd5b6104e56104e036600461459d565b6109d4565b6040516104f291906146c3565b60405180910390f35b610503610a01565b6040516104f291906146ce565b61052361051e3660046145dd565b610a40565b005b61052d610b24565b6040516104f291906147b3565b6104e56105483660046144a8565b610b49565b61050361055b366004614563565b610b60565b610503610bb3565b610570610bcb565b6040516104f29190614652565b6104e561058b3660046143aa565b610bef565b61050361059e366004614563565b610c86565b6105036105b136600461435e565b610c9b565b610503610cee565b610503610d20565b6104e5610d26565b6105236105dc3660046144a8565b610d4a565b6105236105ef36600461457b565b610d87565b610503610dab565b610503610de9565b610570610e0d565b610614610e31565b6040516104f29190614f76565b610503610e36565b610503610e5a565b61052361063f36600461457b565b610e95565b6105236106523660046144a8565b610efa565b6104e56106653660046144a8565b610f96565b61050361067836600461457b565b610fcc565b61050361068b36600461435e565b61119c565b6105036111a7565b6105036106a636600461435e565b6111cb565b61050361121e565b610503611223565b6105036106c936600461435e565b611241565b61050361124c565b6105236106e436600461435e565b61125e565b61052d611279565b6105036106ff36600461435e565b611296565b61050361071236600461435e565b6112e5565b61050361072536600461435e565b61133b565b61057061073836600461444e565b611391565b61050361074b36600461435e565b6113b3565b6105036113ce565b6105036113e0565b61050361076e36600461435e565b6113e6565b6105236107813660046145fe565b611401565b6105236107943660046143aa565b61154c565b6105036115ec565b610503611610565b6104e56107b736600461457b565b611634565b61052d61165d565b6105236107d2366004614563565b61167e565b6105036116ba565b6105036107ed366004614378565b6116cf565b610503611788565b61050361178d565b6104e56108103660046144a8565b61179f565b610523610823366004614563565b6117ee565b6104e56108363660046144a8565b611822565b610503611866565b61050361188a565b61085e610859366004614563565b611890565b6040516104f29190614f07565b61050361087936600461444e565b6118c2565b6105036118d6565b6105036118e2565b61050361089c3660046144d1565b611905565b6105236108af36600461450c565b61197a565b6105236108c2366004614563565b611af6565b610570611b44565b610503611b68565b6105236108e53660046144a8565b611b7a565b6108f2611baf565b6040516104f29190614ee4565b61052361090d366004614563565b611be3565b610503611c4c565b6105236109283660046143e5565b611c5d565b61052361093b36600461457b565b611dea565b610503611e09565b610503611e19565b610503611e1e565b61052361096636600461444e565b611e59565b610503610979366004614378565b611e64565b6104e5611e8f565b610503611ecc565b610503611ede565b610503611f02565b610503611f08565b6105036109b436600461435e565b611f0e565b6105236109c7366004614477565b611f44565b61052d6120c1565b60006001600160e01b03198216637965db0b60e01b14806109f957506109f9826120ef565b90505b919050565b6000610a0b611e8f565b610a1757506000610a3d565b610a1f6142d6565b610a296077612108565b505050604001516001600160f01b03169150505b90565b600080516020614fa5833981519152610a6081610a5b6121ca565b6121ce565b6000196032541415610a8d5760405162461bcd60e51b8152600401610a8490614e3f565b60405180910390fd5b600019603255610a9b611e8f565b610acd57814210610abe5760405162461bcd60e51b8152600401610a84906148c0565b610ac8838361236d565b610b1a565b610ad5612420565b506000610ae06118e2565b9050610aec848461236d565b6000610af66118e2565b9050808214610b175760405162461bcd60e51b8152600401610a8490614e08565b50505b5050600160325550565b60408051808201909152600b81526a0a6e8c2d6cac84088b288b60ab1b602082015290565b6000610b5633848461242e565b5060015b92915050565b6000610b6a6142f6565b50604080518082019091526066546001600160801b03808216808452600160801b9092041660208301819052610baa81610ba487856124e2565b9061253b565b95945050505050565b6000610bbd611e1e565b610bc5610dab565b01905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b60006000196032541415610c155760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610c26848484612595565b610c788433610c73856040518060600160405280602b8152602001615005602b91396001600160a01b038a1660009081526068602090815260408083203384529091529020549190612662565b61242e565b506001806032559392505050565b60009081526020819052604090206001015490565b6000610ca5611e8f565b610cb1575060006109fc565b610cb96142d6565b6001600160a01b0383166000908152607960205260409020610cda906126bc565b602001516001600160f01b03169392505050565b6000806000610cfb612747565b90925090506000610d0c82846127b7565b9050610d1883826127f9565b935050505090565b60675490565b6000610d30611e8f565b8015610d455750606754610d42610cee565b11155b905090565b6000196032541415610d6e5760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610d7e828261283b565b50506001603255565b610d9082610c86565b610d9c81610a5b6121ca565b610da6838361290d565b505050565b6000610db5611e8f565b610dc157506000610a3d565b610dc96142d6565b610dd36077612108565b505050602001516001600160f01b031691505090565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b7f000000000000000000000000000000000000000000000000000000000000000081565b601290565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610e64611e8f565b610e7057506000610a3d565b610e786142d6565b610e82607a6126bc565b604001516001600160f01b031691505090565b610e9d6121ca565b6001600160a01b0316816001600160a01b031614610eec5760405162461bcd60e51b815260040180806020018281038252602f815260200180615111602f913960400191505060405180910390fd5b610ef68282612992565b5050565b600080516020614fc5833981519152610f1581610a5b6121ca565b6000196032541415610f395760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610f498383612a15565b826001600160a01b03167fe96762895ada5c92db22bdc031ae7e0a7122e4f496496dbb44565710e7bd220c8333604051610f84929190614f1e565b60405180910390a25050600160325550565b3360008181526068602090815260408083206001600160a01b03871684529091528120549091610b56918590610c73908661253b565b6000600080516020615099833981519152610fe981610a5b6121ca565b600019603254141561100d5760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611062903090600401614652565b60206040518083038186803b15801561107a57600080fd5b505afa15801561108e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b291906145c5565b9050806110c3576000925050611190565b60006110db60646110d584605f6124e2565b90612ab7565b905060006110e98783612af9565b905060006110f784836127f9565b90508161110b576000955050505050611190565b60006111178583612b0f565b905061114d6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168985612bb7565b7f0ebf205de0d16b8c4e99b7cd8c52f0e40c86547ecda94f93c3fcf75fd040846383898360405161118093929190614f35565b60405180910390a1509094505050505b50600160325592915050565b60006109f982610c9b565b7f9a9a49b990ba9bb39f8048c490a40ab25c18f55d208d5fbcf958261a9b48716d81565b60006111d5611e8f565b6111e1575060006109fc565b6111e96142d6565b6001600160a01b038316600090815260796020526040902061120a906126bc565b604001516001600160f01b03169392505050565b605f81565b796df37f675ef6eadf5ab9a2072d44268d97df79f3b161d474000081565b60006109f98261133b565b60008051602061509983398151915281565b61126a33826000612c09565b61127633826001612c09565b50565b604051806040016040528060018152602001603160f81b81525081565b600060001960325414156112bc5760405162461bcd60e51b8152600401610a8490614e3f565b60001960325560006112cd3361119c565b90506112da338483612ce6565b600160325592915050565b60006112ef611e8f565b6112fb575060006109fc565b6113036142d6565b6001600160a01b038316600090815260766020526040902061132490612108565b505050602001516001600160f01b03169392505050565b6000611345611e8f565b611351575060006109fc565b6113596142d6565b6001600160a01b038316600090815260766020526040902061137a90612108565b505050604001516001600160f01b03169392505050565b60008061139d83612df3565b925050506113ab8482612e2d565b949350505050565b60006113be82610c9b565b6113c7836112e5565b0192915050565b600080516020614fa583398151915281565b60715490565b6001600160a01b03166000908152606a602052604090205490565b600061140b612e58565b9050603354811161144d5760405162461bcd60e51b815260040180806020018281038252602e8152602001806150b9602e913960400191505060405180910390fd5b603381905561145a612e5d565b611462612e6b565b61146d848484612fb5565b611475612fe7565b60408051808201825260128152716459645820536166657479204d6f64756c6560701b6020918201528151808301835260018152603160f81b908201529051469161152a917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f917f054c9f3c830725891f07e6eb30007d2386165490fdad26ce0dd70d9502a9de33917fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc691869130910161475b565b60408051601f1981840301815291905280516020909101206069555050505050565b600080516020614fc583398151915261156781610a5b6121ca565b600019603254141561158b5760405162461bcd60e51b8152600401610a8490614e3f565b60001960325561159c848484612ce6565b836001600160a01b03167f8aabc7295316290cecf4a116d1d8c6d2387df98ff3caa40149f4398d146278d38484336040516115d9939291906146a0565b60405180910390a2505060016032555050565b7f36dc7495d0ae0bc2a620bf292049e4d4e5f800043895b13c08a1977d3a3297f581565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6040805180820190915260078152660e6e8d688b288b60cb1b602082015290565b60001960325414156116a25760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556116b23382612a15565b506001603255565b6b204fce5e3e250261100000006000195b0481565b60007f36dc7495d0ae0bc2a620bf292049e4d4e5f800043895b13c08a1977d3a3297f56116fe81610a5b6121ca565b60001960325414156117225760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556000611734858561303b565b9050846001600160a01b03167f8b787e8c8443ad32d7a6d2aed319d9bee901168951fe414912a3968f977c6a29858333604051611773939291906146a0565b60405180910390a26001603255949350505050565b600081565b600080516020614fc583398151915281565b6000610b563384610c7385604051806060016040528060288152602001615071602891393360009081526068602090815260408083206001600160a01b038d1684529091529020549190612662565b60001960325414156118125760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556116b2338261283b565b600060001960325414156118485760405162461bcd60e51b8152600401610a8490614e3f565b600019603255611859338484612595565b5060018060325592915050565b7f10d8d059343739efce7dad10d09f0806da52b252b3e6a7951920d2d6ec4102e581565b607e5490565b61189861430d565b506000908152607d6020908152604091829020825180840190935280548352600101549082015290565b60006118cf834384611905565b9392505050565b670de0b6b3a764000081565b60008060006118ef612747565b90925090506118fe8183612ab7565b9250505090565b600080600061191384612df3565b506001600160a01b03881660009081526020838152604080832091849052822054939550919350916119479190888461306d565b90506000611962607d607e5489670de0b6b3a764000061306d565b905061196e828261318c565b98975050505050505050565b60007f9a9a49b990ba9bb39f8048c490a40ab25c18f55d208d5fbcf958261a9b48716d8787876040516020016119b3949392919061470b565b6040516020818303038152906040528051906020012090506000606954826040516020016119e2929190614637565b604051602081830303815290604052805190602001209050600060018287878760405160008152602001604052604051611a1f9493929190614787565b6020604051602081039080840390855afa158015611a41573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611a745760405162461bcd60e51b8152600401610a8490614b80565b6001600160a01b0381166000908152606a602052604090208054600181019091558814611ab35760405162461bcd60e51b8152600401610a8490614d92565b86421115611ad35760405162461bcd60e51b8152600401610a8490614bf4565b611adf818a6000612c09565b611aeb818a6001612c09565b505050505050505050565b600080516020614fa5833981519152611b1181610a5b6121ca565b6000196032541415611b355760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610d7e826131f4565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008051602061503083398151915281565b6000196032541415611b9e5760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610d7e338383612ce6565b611bb76142f6565b50604080518082019091526066546001600160801b038082168352600160801b90910416602082015290565b600080516020615030833981519152611bfe81610a5b6121ca565b6000196032541415611c225760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556000611c32611e8f565b15611c4257611c3f612420565b90505b610b1a8382613234565b670de0b6b3a76400006000196116cb565b6001600160a01b038716611c835760405162461bcd60e51b8152600401610a8490614e76565b83421115611ca35760405162461bcd60e51b8152600401610a8490614bbd565b6001600160a01b0387166000908152606a60209081526040808320546069549151909392611cfd917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918d918d918d9189918e91016146d7565b60405160208183030381529060405280519060200120604051602001611d24929190614637565b60405160208183030381529060405280519060200120905060018186868660405160008152602001604052604051611d5f9493929190614787565b6020604051602081039080840390855afa158015611d81573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b031614611dbb5760405162461bcd60e51b8152600401610a8490614979565b611dc682600161253b565b6001600160a01b038a166000908152606a6020526040902055611aeb89898961242e565b611df382610c86565b611dff81610a5b6121ca565b610da68383612992565b6b204fce5e3e2502611000000081565b606481565b6000611e28611e8f565b611e3457506000610a3d565b611e3c6142d6565b611e46607a6126bc565b602001516001600160f01b031691505090565b610ef6338383612c09565b6001600160a01b03918216600090815260686020908152604080832093909416825291909152205490565b6000611e996142f6565b5050604080518082019091526066546001600160801b038082168352600160801b90910416602090910181905242101590565b600080516020614fe583398151915281565b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b607c5490565b60695490565b60006000196032541415611f345760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556112da338361303b565b60007f10d8d059343739efce7dad10d09f0806da52b252b3e6a7951920d2d6ec4102e588886001811115611f7457fe5b8888604051602001611f8a95949392919061472f565b604051602081830303815290604052805190602001209050600060695482604051602001611fb9929190614637565b604051602081830303815290604052805190602001209050600060018287878760405160008152602001604052604051611ff69493929190614787565b6020604051602081039080840390855afa158015612018573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661204b5760405162461bcd60e51b8152600401610a8490614b80565b6001600160a01b0381166000908152606a60205260409020805460018101909155881461208a5760405162461bcd60e51b8152600401610a8490614d92565b864211156120aa5760405162461bcd60e51b8152600401610a8490614bf4565b6120b5818b8b612c09565b50505050505050505050565b604051806040016040528060128152602001716459645820536166657479204d6f64756c6560701b81525081565b6001600160e01b031981166301ffc9a760e01b14919050565b6121106142d6565b600080600061211d6142d6565b5060408051606081018252865461ffff81168083526001600160f01b036201000090920482166020840181905260018a0154909216938301939093529091906000806121676118e2565b855190915061ffff168111156121ba5784604001516001600160f01b031685602001516001600160f01b0316141591506121a08161327f565b61ffff16855260408501516001600160f01b031660208601525b5092989197509550909350915050565b3390565b6121d88282611634565b610ef6576121f0816001600160a01b031660146132a5565b6121fb8360206132a5565b60405160200180807f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081525060170183805190602001908083835b602083106122555780518252601f199092019160209182019101612236565b51815160209384036101000a60001901801990921691161790527001034b99036b4b9b9b4b733903937b6329607d1b919093019081528451601190910192850191508083835b602083106122ba5780518252601f19909201916020918201910161229b565b51815160209384036101000a60001901801990921691161790526040805192909401828103601f190183529384905262461bcd60e51b84526004840181815282516024860152825192975095508594506044909301928601915080838360005b8381101561233257818101518382015260200161231a565b50505050905090810190601f16801561235f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6123756142f6565b604051806040016040528061238985613403565b6001600160801b031681526020016123a084613403565b6001600160801b0390811690915281516066805460208501518416600160801b029284166fffffffffffffffffffffffffffffffff19909116179092161790556040519091507f04821abf6e0e737d3429c8610f8577fd7af8a285e19ac1671673b313e708a71690612413908390614ee4565b60405180910390a1505050565b6000610d456000600161342e565b6001600160a01b0383166124545760405162461bcd60e51b8152600401610a8490614cf4565b6001600160a01b03821661247a5760405162461bcd60e51b8152600401610a8490614c69565b6001600160a01b0380841660008181526068602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906124d59085906146ce565b60405180910390a3505050565b6000826124f157506000610b5a565b828202828482816124fe57fe5b04146118cf5760405162461bcd60e51b81526004018080602001828103825260218152602001806150506021913960400191505060405180910390fd5b6000828201838110156118cf576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6001600160a01b0383166125bb5760405162461bcd60e51b8152600401610a84906149e5565b6001600160a01b0382166125e15760405162461bcd60e51b8152600401610a84906149b0565b806125eb84611241565b10156126095760405162461bcd60e51b8152600401610a8490614ca0565b61261483838361346b565b61261f8383836134b7565b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516124d591906146ce565b600081848411156126b45760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561233257818101518382015260200161231a565b505050900390565b6126c46142d6565b6126cc6142d6565b5060408051606081018252835461ffff811682526001600160f01b03620100009091048116602083015260018501541691810191909152600061270d6118e2565b825190915061ffff16811115612740576127268161327f565b61ffff16825260408201516001600160f01b031660208301525b5092915050565b6000806127526142f6565b50604080518082019091526066546001600160801b03808216808452600160801b90920416602083018190524281111561279e5760405162461bcd60e51b8152600401610a8490614b34565b60006127aa42836127f9565b9295509193505050509091565b60006118cf83836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f000000000000000081525061350d565b60006118cf83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612662565b60006128468261356f565b905061285283826135d9565b61285e600084836134b7565b6128936001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308561360c565b826001600160a01b03167f6c86f3fd5118b3aa8bb4f389a617046de0a3d3d477de1a1673d227f802f616dc3384846040516128d093929190614666565b60405180910390a260405133906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906124d59085906146ce565b6129178282611634565b610ef6576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561294e6121ca565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b61299c8282611634565b15610ef6576000828152602081815260408083206001600160a01b03851684529091529020805460ff191690556129d16121ca565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b612a1d610d26565b15612a3a5760405162461bcd60e51b8152600401610a8490614aa0565b6000612a458361133b565b905080821115612a675760405162461bcd60e51b8152600401610a8490614806565b612a71838361366c565b826001600160a01b03167fe670e4e82118d22a1f9ee18920455ebc958bae26a90a05d31d3378788b1b0e4483604051612aaa91906146ce565b60405180910390a2505050565b60006118cf83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061369d565b6000818310612b0857816118cf565b5090919050565b607c54600090817407ec3daf941806506c5e54eb70c4429fe3b6e5b004821115612b4e57612b4785612b418487612ab7565b906124e2565b9050612b5f565b612b5c846110d584886124e2565b90505b796df37f675ef6eadf5ab9a2072d44268d97df79f3b161d4740000811115612b995760405162461bcd60e51b8152600401610a849061492e565b612ba7607d607e5483613702565b607e55607c819055949350505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610da6908490613780565b6001600160a01b038216612c2f5760405162461bcd60e51b8152600401610a8490614dcb565b6000612c3a82612df3565b925050506000612c49856113b3565b90506000612c578684612e2d565b6001600160a01b03878116600090815260208690526040902080546001600160a01b0319169188169190911790559050612c9381868487613938565b846001600160a01b0316866001600160a01b03167fe8d51c8e11bd570db1734c8ec775785330e77007feed45c43b608ef33ff914bd86604051612cd691906147a5565b60405180910390a3505050505050565b6000612cf184610c9b565b905080821115612d135760405162461bcd60e51b8152600401610a8490614863565b612d1d8483613afb565b612d29846000846134b7565b6000612d3483613b14565b9050612d6a6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168583612bb7565b60405160009033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612d9f9087906146ce565b60405180910390a3846001600160a01b03167fa7c0f0cac6bd4d18042007706c84a8abe823751cf289b69c01e83eef7b5915c7858386604051612de493929190614666565b60405180910390a25050505050565b6000808080846001811115612e0457fe5b1415612e1a5750606b9150606c9050606d612e26565b50606e9150606f905060705b9193909250565b6001600160a01b03808316600090815260208390526040812054909116806118cf5783915050610b5a565b600190565b670de0b6b3a7640000607c55565b612e83600080516020614fe583398151915233613b22565b612e9b60008051602061509983398151915233613b22565b612eb3600080516020614fa583398151915233613b22565b612ecb60008051602061503083398151915233613b22565b612ee3600080516020614fe583398151915280613b2c565b612f09600080516020615099833981519152600080516020614fe5833981519152613b2c565b612f2f600080516020614fa5833981519152600080516020614fe5833981519152613b2c565b612f55600080516020615030833981519152600080516020614fe5833981519152613b2c565b612f8d7f36dc7495d0ae0bc2a620bf292049e4d4e5f800043895b13c08a1977d3a3297f5600080516020614fe5833981519152613b2c565b612fb3600080516020614fc5833981519152600080516020614fe5833981519152613b2c565b565b814210612fd45760405162461bcd60e51b8152600401610a8490614d35565b612fdd816131f4565b610da6838361236d565b613019613014427f0000000000000000000000000000000000000000000000000000000000000000613b76565b613b85565b6072601c6101000a81548163ffffffff021916908363ffffffff160217905550565b600080613046612420565b9050600061305585600161342e565b9050613062858284613bad565b50610baa8585613bc6565b60004383111561308f5760405162461bcd60e51b8152600401610a8490614a5e565b8361309b5750806113ab565b6000808052602086905260409020548310156130b85750806113ab565b600019840160009081526020869052604090205483106130ef575060001983016000908152602085905260409020600101546113ab565b600060001985015b8181111561316f57600282820304810361310f61430d565b50600081815260208981526040918290208251808401909352805480845260019091015491830191909152871415613150576020015193506113ab92505050565b805187111561316157819350613168565b6001820392505b50506130f7565b506000908152602086905260409020600101549050949350505050565b60007812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218311156131d75760006131c383670de0b6b3a7640000612ab7565b90506131cf8482612ab7565b915050610b5a565b6131ed826110d585670de0b6b3a76400006124e2565b9050610b5a565b60678190556040517fb94332f70bda7d9f80755fda0fee46f9fb73433eb08054c482d060b9732a5e37906132299083906146ce565b60405180910390a150565b61323d81613c7f565b5060718290556040517ffd301ea009c64d5832f2d8f8d8f632dda101449dd7bab7e219a7d4fe924f190a906132739084906146ce565b60405180910390a15050565b60008161ffff811681146109f95760405162461bcd60e51b8152600401610a8490614afd565b6060808260020260020167ffffffffffffffff811180156132c557600080fd5b506040519080825280601f01601f1916602001820160405280156132f0576020820181803683370190505b509050600360fc1b8160008151811061330557fe5b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061332e57fe5b60200101906001600160f81b031916908160001a905350600160028402015b60018111156133af576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061337857fe5b1a60f81b82828151811061338857fe5b60200101906001600160f81b031916908160001a90535060049490941c936000190161334d565b5083156118cf576040805162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015290519081900360640190fd5b6000816001600160801b03811681146109f95760405162461bcd60e51b8152600401610a84906148f7565b60008061343b8484613c8b565b90506134456142d6565b613450828686613cfe565b60208101519091506001600160f01b0316610baa8383613d70565b6000613475612420565b9050600061348585600185613dc0565b9050600061349585600186613e4b565b90506134a2868385613bad565b506134ae858285613bad565b50505050505050565b60006134c484606d612e2d565b905060006134d384606d612e2d565b90506134e28282856000613938565b60006134ef866070612e2d565b905060006134fe866070612e2d565b90506134ae8282876001613938565b6000818361355c5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561233257818101518382015260200161231a565b5082848161356657fe5b06949350505050565b607c546000907407ec3daf941806506c5e54eb70c4429fe3b6e5b0048111156135bb5760006135a682670de0b6b3a7640000612ab7565b90506135b284826124e2565b925050506109fc565b6135d1670de0b6b3a76400006110d585846124e2565b9150506109fc565b60006135e86000600184613e4b565b905060006135f884600185613e4b565b9050613605848284613bad565b5050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052613666908590613780565b50505050565b6136796000600183613eaf565b61368582600183613eaf565b61369160008083613f0d565b610ef682600083613f0d565b600081836136ec5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561233257818101518382015260200161231a565b5060008385816136f857fe5b0495945050505050565b60004383158015906137265750600019840160009081526020869052604090205481145b1561374c57505060001982016000908152602084905260409020600101819055816118cf565b60408051808201825291825260208083018581526000878152918890529190209151825551600191820155830190506118cf565b613792826001600160a01b0316613f53565b6137e3576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106138215780518252601f199092019160209182019101613802565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613883576040519150601f19603f3d011682016040523d82523d6000602084013e613888565b606091505b5091509150816138df576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613666578080602001905160208110156138fb57600080fd5b50516136665760405162461bcd60e51b815260040180806020018281038252602a8152602001806150e7602a913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b0316141561395757613666565b60008061396383612df3565b5090925090506001600160a01b03861615613a2e576001600160a01b03861660009081526020838152604080832091849052822054909181156139b9575060001981016000908152602083905260409020600101545b60006139c582896127f9565b90506139d2848483613702565b6001600160a01b038b16600081815260208890526040908190209290925590517fa0a19463ee116110c9b282012d9b65cc5522dc38a9520340cbaf3142e550127f90613a219084908b90614f54565b60405180910390a2505050505b6001600160a01b03851615613af3576001600160a01b0385166000908152602083815260408083209184905282205490918115613a7e575060001981016000908152602083905260409020600101545b6000613a8a828961253b565b9050613a97848483613702565b6001600160a01b038a16600081815260208890526040908190209290925590517fa0a19463ee116110c9b282012d9b65cc5522dc38a9520340cbaf3142e550127f90613ae69084908b90614f54565b60405180910390a2505050505b505050505050565b613b0760008083613dc0565b50610da682600083613dc0565b60006109f982607c5461318c565b610ef6828261290d565b80613b3683610c86565b60405184907fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff90600090a460009182526020829052604090912060010155565b6000818311612b0857816118cf565b60008163ffffffff811681146109f95760405162461bcd60e51b8152600401610a8490614c32565b600080613bb983613c7f565b9050610baa858583613f8c565b6001600160a01b0380831660009081526074602052604081208054908290559091613c35907f0000000000000000000000000000000000000000000000000000000000000000167f0000000000000000000000000000000000000000000000000000000000000000858461360c565b836001600160a01b03167f2ef606d064225d24c1514dc94907c134faee1237445c2f63f410cce0852b20548483604051613c70929190614687565b60405180910390a29392505050565b60006109f9824261407f565b60008115613cc8576001600160a01b03831615613cc057506001600160a01b0382166000908152607660205260409020610b5a565b506077610b5a565b6001600160a01b03831615613cf557506001600160a01b0382166000908152607960205260409020610b5a565b50607a92915050565b613d066142d6565b8115613d6757613d146142d6565b6000806000613d2288612108565b93509350935093508015613d5b576001600160a01b038716613d4e57613d48828461422c565b50613d5b565b613d59878385614266565b505b839450505050506118cf565b6113ab846126bc565b80518254602083015161ffff1990911661ffff92831617909116620100006001600160f01b0392831602178355604090910151600190920180546001600160f01b03191692909116919091179055565b600080613dcd8585613c8b565b9050613dd76142d6565b613de2828787613cfe565b60208101519091506001600160f01b0316613e05613e0082876127f9565b614280565b6001600160f01b0390811660208401526040830151613e2991613e009116876127f9565b6001600160f01b03166040830152613e418383613d70565b9695505050505050565b600080613e588585613c8b565b9050613e626142d6565b613e6d828787613cfe565b60208101519091506001600160f01b0316613e8b613e00828761253b565b6001600160f01b0390811660208401526040830151613e2991613e0091168761253b565b6000613ebb8484613c8b565b9050613ec56142d6565b613ed0828686613cfe565b9050613ef5613e008483604001516001600160f01b03166127f990919063ffffffff16565b6001600160f01b031660408201526136058282613d70565b6000613f198484613c8b565b9050613f236142d6565b613f2e828686613cfe565b9050613ef5613e008483604001516001600160f01b031661253b90919063ffffffff16565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906113ab575050151592915050565b6001600160a01b038316600090815260746020908152604080832054607390925282205483811415613fc0575090506118cf565b600085613fce575081614020565b6000613fda86846127f9565b90506000613ff4670de0b6b3a76400006110d58a856124e2565b9050614000858261253b565b6001600160a01b038a166000908152607460205260409020819055925050505b6001600160a01b03871660008181526073602052604090819020879055517ff2c02e23a652c66023e40b9cf4d657ebb15f9235c261a02f740a9fd7a0e5bed29061406d9088908590614f68565b60405180910390a29695505050505050565b6072546000906001600160e01b03811690600160e01b900463ffffffff16826140c8857f0000000000000000000000000000000000000000000000000000000000000000612af9565b90508181116140dc57829350505050610b5a565b6071548015806140ea575086155b1561415b576140f882613b85565b6072601c6101000a81548163ffffffff021916908363ffffffff1602179055507fb9b54fb40571ef7044b07522f579f84f94c6a561ca45129676901ff7781f6d0d8460405161414791906146ce565b60405180910390a183945050505050610b5a565b600061416783856127f9565b90506000614185896110d5670de0b6b3a7640000612b4186886124e2565b90506000614193878361253b565b905061419e85613b85565b6072601c6101000a81548163ffffffff021916908363ffffffff1602179055506141c7816142ab565b607280546001600160e01b0319166001600160e01b03929092169190911790556040517fb9b54fb40571ef7044b07522f579f84f94c6a561ca45129676901ff7781f6d0d906142179083906146ce565b60405180910390a19998505050505050505050565b60008061423d61055b84600161253b565b9050600061424b858361407f565b60008581526075602052604090208190559250505092915050565b600081815260756020526040812054610baa858583613f8c565b6000816001600160f01b03811681146109f95760405162461bcd60e51b8152600401610a8490614ead565b6000816001600160e01b03811681146109f95760405162461bcd60e51b8152600401610a8490614a27565b604080516060810182526000808252602082018190529181019190915290565b604080518082019091526000808252602082015290565b604051806040016040528060008152602001600081525090565b80356001600160a01b03811681146109fc57600080fd5b8035600281106109fc57600080fd5b803560ff811681146109fc57600080fd5b60006020828403121561436f578081fd5b6118cf82614327565b6000806040838503121561438a578081fd5b61439383614327565b91506143a160208401614327565b90509250929050565b6000806000606084860312156143be578081fd5b6143c784614327565b92506143d560208501614327565b9150604084013590509250925092565b600080600080600080600060e0888a0312156143ff578283fd5b61440888614327565b965061441660208901614327565b955060408801359450606088013593506144326080890161434d565b925060a0880135915060c0880135905092959891949750929550565b60008060408385031215614460578182fd5b61446983614327565b91506143a16020840161433e565b600080600080600080600060e0888a031215614491578283fd5b61449a88614327565b96506144166020890161433e565b600080604083850312156144ba578182fd5b6144c383614327565b946020939093013593505050565b6000806000606084860312156144e5578283fd5b6144ee84614327565b9250602084013591506145036040850161433e565b90509250925092565b60008060008060008060c08789031215614524578182fd5b61452d87614327565b955060208701359450604087013593506145496060880161434d565b92506080870135915060a087013590509295509295509295565b600060208284031215614574578081fd5b5035919050565b6000806040838503121561458d578182fd5b823591506143a160208401614327565b6000602082840312156145ae578081fd5b81356001600160e01b0319811681146118cf578182fd5b6000602082840312156145d6578081fd5b5051919050565b600080604083850312156145ef578182fd5b50508035926020909101359150565b600080600060608486031215614612578081fd5b505081359360208301359350604090920135919050565b6002811061463357fe5b9052565b61190160f01b81526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b901515815260200190565b90815260200190565b9586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b9384526001600160a01b039290921660208401526040830152606082015260800190565b9485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b93845260ff9290921660208401526040830152606082015260800190565b60208101610b5a8284614629565b6000602080835283518082850152825b818110156147df578581018301518582016040015282016147c3565b818111156147f05783604083870101525b50601f01601f1916929092016040019392505050565b60208082526038908201527f534d315374616b696e673a20576974686472617720726571756573742065786360408201527f65656473206e657874206163746976652062616c616e63650000000000000000606082015260800190565b6020808252603b908201527f534d315374616b696e673a20576974686472617720616d6f756e74206578636560408201527f656473207374616b657220696e6163746976652062616c616e63650000000000606082015260800190565b6020808252601c908201527f534d3141646d696e3a20537461727465642065706f6368207a65726f00000000604082015260600190565b6020808252601c908201527f53616665436173743a20746f55696e74313238206f766572666c6f7700000000604082015260600190565b6020808252602b908201527f534d3145786368616e6765526174653a204d61782065786368616e676520726160408201526a1d1948195e18d95959195960aa1b606082015260800190565b6020808252601b908201527f534d3145524332303a20494e56414c49445f5349474e41545552450000000000604082015260600190565b6020808252818101527f534d3145524332303a205472616e7366657220746f2061646472657373283029604082015260600190565b60208082526022908201527f534d3145524332303a205472616e736665722066726f6d206164647265737328604082015261302960f01b606082015260800190565b6020808252601c908201527f53616665436173743a20746f55696e74323234206f766572666c6f7700000000604082015260600190565b60208082526022908201527f534d31536e617073686f74733a20494e56414c49445f424c4f434b5f4e554d4260408201526122a960f11b606082015260800190565b6020808252603f908201527f534d315374616b696e673a20576974686472617720726571756573747320726560408201527f737472696374656420696e2074686520626c61636b6f75742077696e646f7700606082015260800190565b6020808252601b908201527f53616665436173743a20746f55696e743136206f766572666c6f770000000000604082015260600190565b6020808252602c908201527f534d3145706f63685363686564756c653a2045706f6368207a65726f2068617360408201526b081b9bdd081cdd185c9d195960a21b606082015260800190565b6020808252602f90820152600080516020614f8583398151915260408201526e56414c49445f5349474e415455524560881b606082015260800190565b6020808252601c908201527f534d3145524332303a20494e56414c49445f45585049524154494f4e00000000604082015260600190565b6020808252603090820152600080516020614f8583398151915260408201526f2b20a624a22fa2ac2824a920aa24a7a760811b606082015260800190565b6020808252601b908201527f53616665436173743a20746f55696e743332206f766572666c6f770000000000604082015260600190565b6020808252601f908201527f534d3145524332303a20417070726f766520746f206164647265737328302900604082015260600190565b60208082526034908201527f534d3145524332303a205472616e736665722065786365656473206e6578742060408201527365706f6368206163746976652062616c616e636560601b606082015260800190565b60208082526021908201527f534d3145524332303a20417070726f76652066726f6d206164647265737328306040820152602960f81b606082015260800190565b6020808252603c908201527f534d3145706f63685363686564756c653a2045706f6368207a65726f206d757360408201527f7420737461727420616674657220696e697469616c697a6174696f6e00000000606082015260800190565b6020808252602b90820152600080516020614f8583398151915260408201526a56414c49445f4e4f4e434560a81b606082015260800190565b6020808252602f90820152600080516020614f8583398151915260408201526e56414c49445f44454c45474154454560881b606082015260800190565b60208082526018908201527f534d3141646d696e3a204368616e6765642065706f6368730000000000000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526017908201527f534d3145524332303a20494e56414c49445f4f574e4552000000000000000000604082015260600190565b6020808252601c908201527f53616665436173743a20746f55696e74323430206f766572666c6f7700000000604082015260600190565b81516001600160801b039081168252602092830151169181019190915260400190565b815181526020918201519181019190915260400190565b9182526001600160a01b0316602082015260400190565b9283526001600160a01b03919091166020830152604082015260600190565b828152604081016118cf6020830184614629565b918252602082015260400190565b60ff9190911681526020019056fe534d31476f7665726e616e6365506f77657244656c65676174696f6e3a20494ea69ba352872fe0ee634bc8d48d2a09a61267da1bfb2015e67a11ad05fe21f04ba6fbd0d4ef0ac50b4de984ab8f303863596293cce6d67dd6111979bcf56abe74b19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e534d3145524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636574ec845281a5bcabeef9a800a79d30928ff9e6f2dc6f69a233fc39a83cb81ed2536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77534d3145524332303a2044656372656173656420616c6c6f77616e63652062656c6f77207a65726f12b42e8a160f6064dc959c6f251e3af0750ad213dbecf573b4710d67d6c28e39436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a65645361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a2646970667358221220be2c7ace03c1f2297e54cd4c228002a6bdb85a5a2ae8483703ae308941ef9c6e64736f6c6343000705003300000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff500000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff5000000000000000000000000639192d54431f8c816368d3fb4107bc168d0e871000000000000000000000000000000000000000000000000000000006138cff0000000000000000000000000000000000000000000000000000000006a9ed16e

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104cd5760003560e01c806380d8591111610283578063c4987fd21161015c578063d8a3f3be116100ce578063e614d0d611610092578063e614d0d61461098e578063e6aa216c14610996578063ed24911d1461099e578063ef5cfb8c146109a6578063f713d8a8146109b9578063fd070296146109cc576104cd565b8063d8a3f3be14610950578063dc937e1c14610958578063dd62ed3e1461096b578063e294121e1461097e578063e58378bb14610986576104cd565b8063cbeb09aa11610120578063cbeb09aa146108ff578063d057152c14610912578063d505accf1461091a578063d547741f1461092d578063d744bd6714610940578063d837d18714610948576104cd565b8063c4987fd2146108b4578063c5601072146108c7578063c5b39666146108cf578063c6066272146108d7578063c89b0fff146108ea576104cd565b8063a457c2d7116101f5578063b29d3dbb116101b9578063b29d3dbb1461084b578063b2f4201d1461086b578063b7b038da1461087e578063b97dd9e214610886578063c2ffbb911461088e578063c3cda520146108a1576104cd565b8063a457c2d714610802578063a694fc3a14610815578063a9059cbb14610828578063aa9fbe021461083b578063b0d266bd14610843576104cd565b806395d89b411161024757806395d89b41146107bc5780639ee679e8146107c45780639fb2907f146107d7578063a1663340146107df578063a217fddf146107f2578063a2f44db0146107fa576104cd565b806380d85911146107735780638e4c4d78146107865780638eebb0fd14610799578063919cd40f146107a157806391d14854146107a9576104cd565b806335de00ab116103b55780635095af64116103275780636461d4bf116102eb5780636461d4bf146107175780636f50458d1461072a57806370a082311461073d57806373f93fbd1461075057806376f259b2146107585780637ecebe0014610760576104cd565b80635095af64146106ce5780635c19a95c146106d65780635cc33321146106e95780635da51e1f146106f1578063639b8d6214610704576104cd565b8063408aabc711610379578063408aabc71461067d57806341cbf54a1461069057806344501f981461069857806349f9d8e5146106ab5780634b35073f146106b35780634c0bcfe5146106bb576104cd565b806335de00ab1461062957806336568abe146106315780633658aa251461064457806339509351146106575780633d82e3c11461066a576104cd565b80632495c2ab1161044e5780632f2ff15d116104125780632f2ff15d146105e15780633051f940146105f457806330adf81f146105fc578063312f6b8314610604578063313ce5671461060c57806333c56e0b14610621576104cd565b80632495c2ab146105a35780632847f6ff146105b657806329b103d8146105be5780632c73c1a1146105c65780632ee40908146105ce576104cd565b8063125f844011610495578063125f84401461054d57806318160ddd146105605780631c89553c1461056857806323b872dd1461057d578063248a9ca314610590576104cd565b806301ffc9a7146104d257806303f637f6146104fb57806306ae63ae1461051057806306fdde0314610525578063095ea7b31461053a575b600080fd5b6104e56104e036600461459d565b6109d4565b6040516104f291906146c3565b60405180910390f35b610503610a01565b6040516104f291906146ce565b61052361051e3660046145dd565b610a40565b005b61052d610b24565b6040516104f291906147b3565b6104e56105483660046144a8565b610b49565b61050361055b366004614563565b610b60565b610503610bb3565b610570610bcb565b6040516104f29190614652565b6104e561058b3660046143aa565b610bef565b61050361059e366004614563565b610c86565b6105036105b136600461435e565b610c9b565b610503610cee565b610503610d20565b6104e5610d26565b6105236105dc3660046144a8565b610d4a565b6105236105ef36600461457b565b610d87565b610503610dab565b610503610de9565b610570610e0d565b610614610e31565b6040516104f29190614f76565b610503610e36565b610503610e5a565b61052361063f36600461457b565b610e95565b6105236106523660046144a8565b610efa565b6104e56106653660046144a8565b610f96565b61050361067836600461457b565b610fcc565b61050361068b36600461435e565b61119c565b6105036111a7565b6105036106a636600461435e565b6111cb565b61050361121e565b610503611223565b6105036106c936600461435e565b611241565b61050361124c565b6105236106e436600461435e565b61125e565b61052d611279565b6105036106ff36600461435e565b611296565b61050361071236600461435e565b6112e5565b61050361072536600461435e565b61133b565b61057061073836600461444e565b611391565b61050361074b36600461435e565b6113b3565b6105036113ce565b6105036113e0565b61050361076e36600461435e565b6113e6565b6105236107813660046145fe565b611401565b6105236107943660046143aa565b61154c565b6105036115ec565b610503611610565b6104e56107b736600461457b565b611634565b61052d61165d565b6105236107d2366004614563565b61167e565b6105036116ba565b6105036107ed366004614378565b6116cf565b610503611788565b61050361178d565b6104e56108103660046144a8565b61179f565b610523610823366004614563565b6117ee565b6104e56108363660046144a8565b611822565b610503611866565b61050361188a565b61085e610859366004614563565b611890565b6040516104f29190614f07565b61050361087936600461444e565b6118c2565b6105036118d6565b6105036118e2565b61050361089c3660046144d1565b611905565b6105236108af36600461450c565b61197a565b6105236108c2366004614563565b611af6565b610570611b44565b610503611b68565b6105236108e53660046144a8565b611b7a565b6108f2611baf565b6040516104f29190614ee4565b61052361090d366004614563565b611be3565b610503611c4c565b6105236109283660046143e5565b611c5d565b61052361093b36600461457b565b611dea565b610503611e09565b610503611e19565b610503611e1e565b61052361096636600461444e565b611e59565b610503610979366004614378565b611e64565b6104e5611e8f565b610503611ecc565b610503611ede565b610503611f02565b610503611f08565b6105036109b436600461435e565b611f0e565b6105236109c7366004614477565b611f44565b61052d6120c1565b60006001600160e01b03198216637965db0b60e01b14806109f957506109f9826120ef565b90505b919050565b6000610a0b611e8f565b610a1757506000610a3d565b610a1f6142d6565b610a296077612108565b505050604001516001600160f01b03169150505b90565b600080516020614fa5833981519152610a6081610a5b6121ca565b6121ce565b6000196032541415610a8d5760405162461bcd60e51b8152600401610a8490614e3f565b60405180910390fd5b600019603255610a9b611e8f565b610acd57814210610abe5760405162461bcd60e51b8152600401610a84906148c0565b610ac8838361236d565b610b1a565b610ad5612420565b506000610ae06118e2565b9050610aec848461236d565b6000610af66118e2565b9050808214610b175760405162461bcd60e51b8152600401610a8490614e08565b50505b5050600160325550565b60408051808201909152600b81526a0a6e8c2d6cac84088b288b60ab1b602082015290565b6000610b5633848461242e565b5060015b92915050565b6000610b6a6142f6565b50604080518082019091526066546001600160801b03808216808452600160801b9092041660208301819052610baa81610ba487856124e2565b9061253b565b95945050505050565b6000610bbd611e1e565b610bc5610dab565b01905090565b7f000000000000000000000000639192d54431f8c816368d3fb4107bc168d0e87181565b60006000196032541415610c155760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610c26848484612595565b610c788433610c73856040518060600160405280602b8152602001615005602b91396001600160a01b038a1660009081526068602090815260408083203384529091529020549190612662565b61242e565b506001806032559392505050565b60009081526020819052604090206001015490565b6000610ca5611e8f565b610cb1575060006109fc565b610cb96142d6565b6001600160a01b0383166000908152607960205260409020610cda906126bc565b602001516001600160f01b03169392505050565b6000806000610cfb612747565b90925090506000610d0c82846127b7565b9050610d1883826127f9565b935050505090565b60675490565b6000610d30611e8f565b8015610d455750606754610d42610cee565b11155b905090565b6000196032541415610d6e5760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610d7e828261283b565b50506001603255565b610d9082610c86565b610d9c81610a5b6121ca565b610da6838361290d565b505050565b6000610db5611e8f565b610dc157506000610a3d565b610dc96142d6565b610dd36077612108565b505050602001516001600160f01b031691505090565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b7f00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff581565b601290565b7f000000000000000000000000000000000000000000000000000000006138cff081565b6000610e64611e8f565b610e7057506000610a3d565b610e786142d6565b610e82607a6126bc565b604001516001600160f01b031691505090565b610e9d6121ca565b6001600160a01b0316816001600160a01b031614610eec5760405162461bcd60e51b815260040180806020018281038252602f815260200180615111602f913960400191505060405180910390fd5b610ef68282612992565b5050565b600080516020614fc5833981519152610f1581610a5b6121ca565b6000196032541415610f395760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610f498383612a15565b826001600160a01b03167fe96762895ada5c92db22bdc031ae7e0a7122e4f496496dbb44565710e7bd220c8333604051610f84929190614f1e565b60405180910390a25050600160325550565b3360008181526068602090815260408083206001600160a01b03871684529091528120549091610b56918590610c73908661253b565b6000600080516020615099833981519152610fe981610a5b6121ca565b600019603254141561100d5760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556040516370a0823160e01b81526000906001600160a01b037f00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff516906370a0823190611062903090600401614652565b60206040518083038186803b15801561107a57600080fd5b505afa15801561108e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b291906145c5565b9050806110c3576000925050611190565b60006110db60646110d584605f6124e2565b90612ab7565b905060006110e98783612af9565b905060006110f784836127f9565b90508161110b576000955050505050611190565b60006111178583612b0f565b905061114d6001600160a01b037f00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff5168985612bb7565b7f0ebf205de0d16b8c4e99b7cd8c52f0e40c86547ecda94f93c3fcf75fd040846383898360405161118093929190614f35565b60405180910390a1509094505050505b50600160325592915050565b60006109f982610c9b565b7f9a9a49b990ba9bb39f8048c490a40ab25c18f55d208d5fbcf958261a9b48716d81565b60006111d5611e8f565b6111e1575060006109fc565b6111e96142d6565b6001600160a01b038316600090815260796020526040902061120a906126bc565b604001516001600160f01b03169392505050565b605f81565b796df37f675ef6eadf5ab9a2072d44268d97df79f3b161d474000081565b60006109f98261133b565b60008051602061509983398151915281565b61126a33826000612c09565b61127633826001612c09565b50565b604051806040016040528060018152602001603160f81b81525081565b600060001960325414156112bc5760405162461bcd60e51b8152600401610a8490614e3f565b60001960325560006112cd3361119c565b90506112da338483612ce6565b600160325592915050565b60006112ef611e8f565b6112fb575060006109fc565b6113036142d6565b6001600160a01b038316600090815260766020526040902061132490612108565b505050602001516001600160f01b03169392505050565b6000611345611e8f565b611351575060006109fc565b6113596142d6565b6001600160a01b038316600090815260766020526040902061137a90612108565b505050604001516001600160f01b03169392505050565b60008061139d83612df3565b925050506113ab8482612e2d565b949350505050565b60006113be82610c9b565b6113c7836112e5565b0192915050565b600080516020614fa583398151915281565b60715490565b6001600160a01b03166000908152606a602052604090205490565b600061140b612e58565b9050603354811161144d5760405162461bcd60e51b815260040180806020018281038252602e8152602001806150b9602e913960400191505060405180910390fd5b603381905561145a612e5d565b611462612e6b565b61146d848484612fb5565b611475612fe7565b60408051808201825260128152716459645820536166657479204d6f64756c6560701b6020918201528151808301835260018152603160f81b908201529051469161152a917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f917f054c9f3c830725891f07e6eb30007d2386165490fdad26ce0dd70d9502a9de33917fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc691869130910161475b565b60408051601f1981840301815291905280516020909101206069555050505050565b600080516020614fc583398151915261156781610a5b6121ca565b600019603254141561158b5760405162461bcd60e51b8152600401610a8490614e3f565b60001960325561159c848484612ce6565b836001600160a01b03167f8aabc7295316290cecf4a116d1d8c6d2387df98ff3caa40149f4398d146278d38484336040516115d9939291906146a0565b60405180910390a2505060016032555050565b7f36dc7495d0ae0bc2a620bf292049e4d4e5f800043895b13c08a1977d3a3297f581565b7f000000000000000000000000000000000000000000000000000000006a9ed16e81565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6040805180820190915260078152660e6e8d688b288b60cb1b602082015290565b60001960325414156116a25760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556116b23382612a15565b506001603255565b6b204fce5e3e250261100000006000195b0481565b60007f36dc7495d0ae0bc2a620bf292049e4d4e5f800043895b13c08a1977d3a3297f56116fe81610a5b6121ca565b60001960325414156117225760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556000611734858561303b565b9050846001600160a01b03167f8b787e8c8443ad32d7a6d2aed319d9bee901168951fe414912a3968f977c6a29858333604051611773939291906146a0565b60405180910390a26001603255949350505050565b600081565b600080516020614fc583398151915281565b6000610b563384610c7385604051806060016040528060288152602001615071602891393360009081526068602090815260408083206001600160a01b038d1684529091529020549190612662565b60001960325414156118125760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556116b2338261283b565b600060001960325414156118485760405162461bcd60e51b8152600401610a8490614e3f565b600019603255611859338484612595565b5060018060325592915050565b7f10d8d059343739efce7dad10d09f0806da52b252b3e6a7951920d2d6ec4102e581565b607e5490565b61189861430d565b506000908152607d6020908152604091829020825180840190935280548352600101549082015290565b60006118cf834384611905565b9392505050565b670de0b6b3a764000081565b60008060006118ef612747565b90925090506118fe8183612ab7565b9250505090565b600080600061191384612df3565b506001600160a01b03881660009081526020838152604080832091849052822054939550919350916119479190888461306d565b90506000611962607d607e5489670de0b6b3a764000061306d565b905061196e828261318c565b98975050505050505050565b60007f9a9a49b990ba9bb39f8048c490a40ab25c18f55d208d5fbcf958261a9b48716d8787876040516020016119b3949392919061470b565b6040516020818303038152906040528051906020012090506000606954826040516020016119e2929190614637565b604051602081830303815290604052805190602001209050600060018287878760405160008152602001604052604051611a1f9493929190614787565b6020604051602081039080840390855afa158015611a41573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611a745760405162461bcd60e51b8152600401610a8490614b80565b6001600160a01b0381166000908152606a602052604090208054600181019091558814611ab35760405162461bcd60e51b8152600401610a8490614d92565b86421115611ad35760405162461bcd60e51b8152600401610a8490614bf4565b611adf818a6000612c09565b611aeb818a6001612c09565b505050505050505050565b600080516020614fa5833981519152611b1181610a5b6121ca565b6000196032541415611b355760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610d7e826131f4565b7f00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff581565b60008051602061503083398151915281565b6000196032541415611b9e5760405162461bcd60e51b8152600401610a8490614e3f565b600019603255610d7e338383612ce6565b611bb76142f6565b50604080518082019091526066546001600160801b038082168352600160801b90910416602082015290565b600080516020615030833981519152611bfe81610a5b6121ca565b6000196032541415611c225760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556000611c32611e8f565b15611c4257611c3f612420565b90505b610b1a8382613234565b670de0b6b3a76400006000196116cb565b6001600160a01b038716611c835760405162461bcd60e51b8152600401610a8490614e76565b83421115611ca35760405162461bcd60e51b8152600401610a8490614bbd565b6001600160a01b0387166000908152606a60209081526040808320546069549151909392611cfd917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918d918d918d9189918e91016146d7565b60405160208183030381529060405280519060200120604051602001611d24929190614637565b60405160208183030381529060405280519060200120905060018186868660405160008152602001604052604051611d5f9493929190614787565b6020604051602081039080840390855afa158015611d81573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b031614611dbb5760405162461bcd60e51b8152600401610a8490614979565b611dc682600161253b565b6001600160a01b038a166000908152606a6020526040902055611aeb89898961242e565b611df382610c86565b611dff81610a5b6121ca565b610da68383612992565b6b204fce5e3e2502611000000081565b606481565b6000611e28611e8f565b611e3457506000610a3d565b611e3c6142d6565b611e46607a6126bc565b602001516001600160f01b031691505090565b610ef6338383612c09565b6001600160a01b03918216600090815260686020908152604080832093909416825291909152205490565b6000611e996142f6565b5050604080518082019091526066546001600160801b038082168352600160801b90910416602090910181905242101590565b600080516020614fe583398151915281565b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b607c5490565b60695490565b60006000196032541415611f345760405162461bcd60e51b8152600401610a8490614e3f565b6000196032556112da338361303b565b60007f10d8d059343739efce7dad10d09f0806da52b252b3e6a7951920d2d6ec4102e588886001811115611f7457fe5b8888604051602001611f8a95949392919061472f565b604051602081830303815290604052805190602001209050600060695482604051602001611fb9929190614637565b604051602081830303815290604052805190602001209050600060018287878760405160008152602001604052604051611ff69493929190614787565b6020604051602081039080840390855afa158015612018573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661204b5760405162461bcd60e51b8152600401610a8490614b80565b6001600160a01b0381166000908152606a60205260409020805460018101909155881461208a5760405162461bcd60e51b8152600401610a8490614d92565b864211156120aa5760405162461bcd60e51b8152600401610a8490614bf4565b6120b5818b8b612c09565b50505050505050505050565b604051806040016040528060128152602001716459645820536166657479204d6f64756c6560701b81525081565b6001600160e01b031981166301ffc9a760e01b14919050565b6121106142d6565b600080600061211d6142d6565b5060408051606081018252865461ffff81168083526001600160f01b036201000090920482166020840181905260018a0154909216938301939093529091906000806121676118e2565b855190915061ffff168111156121ba5784604001516001600160f01b031685602001516001600160f01b0316141591506121a08161327f565b61ffff16855260408501516001600160f01b031660208601525b5092989197509550909350915050565b3390565b6121d88282611634565b610ef6576121f0816001600160a01b031660146132a5565b6121fb8360206132a5565b60405160200180807f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081525060170183805190602001908083835b602083106122555780518252601f199092019160209182019101612236565b51815160209384036101000a60001901801990921691161790527001034b99036b4b9b9b4b733903937b6329607d1b919093019081528451601190910192850191508083835b602083106122ba5780518252601f19909201916020918201910161229b565b51815160209384036101000a60001901801990921691161790526040805192909401828103601f190183529384905262461bcd60e51b84526004840181815282516024860152825192975095508594506044909301928601915080838360005b8381101561233257818101518382015260200161231a565b50505050905090810190601f16801561235f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6123756142f6565b604051806040016040528061238985613403565b6001600160801b031681526020016123a084613403565b6001600160801b0390811690915281516066805460208501518416600160801b029284166fffffffffffffffffffffffffffffffff19909116179092161790556040519091507f04821abf6e0e737d3429c8610f8577fd7af8a285e19ac1671673b313e708a71690612413908390614ee4565b60405180910390a1505050565b6000610d456000600161342e565b6001600160a01b0383166124545760405162461bcd60e51b8152600401610a8490614cf4565b6001600160a01b03821661247a5760405162461bcd60e51b8152600401610a8490614c69565b6001600160a01b0380841660008181526068602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906124d59085906146ce565b60405180910390a3505050565b6000826124f157506000610b5a565b828202828482816124fe57fe5b04146118cf5760405162461bcd60e51b81526004018080602001828103825260218152602001806150506021913960400191505060405180910390fd5b6000828201838110156118cf576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6001600160a01b0383166125bb5760405162461bcd60e51b8152600401610a84906149e5565b6001600160a01b0382166125e15760405162461bcd60e51b8152600401610a84906149b0565b806125eb84611241565b10156126095760405162461bcd60e51b8152600401610a8490614ca0565b61261483838361346b565b61261f8383836134b7565b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516124d591906146ce565b600081848411156126b45760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561233257818101518382015260200161231a565b505050900390565b6126c46142d6565b6126cc6142d6565b5060408051606081018252835461ffff811682526001600160f01b03620100009091048116602083015260018501541691810191909152600061270d6118e2565b825190915061ffff16811115612740576127268161327f565b61ffff16825260408201516001600160f01b031660208301525b5092915050565b6000806127526142f6565b50604080518082019091526066546001600160801b03808216808452600160801b90920416602083018190524281111561279e5760405162461bcd60e51b8152600401610a8490614b34565b60006127aa42836127f9565b9295509193505050509091565b60006118cf83836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f000000000000000081525061350d565b60006118cf83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612662565b60006128468261356f565b905061285283826135d9565b61285e600084836134b7565b6128936001600160a01b037f00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff51633308561360c565b826001600160a01b03167f6c86f3fd5118b3aa8bb4f389a617046de0a3d3d477de1a1673d227f802f616dc3384846040516128d093929190614666565b60405180910390a260405133906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906124d59085906146ce565b6129178282611634565b610ef6576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561294e6121ca565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b61299c8282611634565b15610ef6576000828152602081815260408083206001600160a01b03851684529091529020805460ff191690556129d16121ca565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b612a1d610d26565b15612a3a5760405162461bcd60e51b8152600401610a8490614aa0565b6000612a458361133b565b905080821115612a675760405162461bcd60e51b8152600401610a8490614806565b612a71838361366c565b826001600160a01b03167fe670e4e82118d22a1f9ee18920455ebc958bae26a90a05d31d3378788b1b0e4483604051612aaa91906146ce565b60405180910390a2505050565b60006118cf83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061369d565b6000818310612b0857816118cf565b5090919050565b607c54600090817407ec3daf941806506c5e54eb70c4429fe3b6e5b004821115612b4e57612b4785612b418487612ab7565b906124e2565b9050612b5f565b612b5c846110d584886124e2565b90505b796df37f675ef6eadf5ab9a2072d44268d97df79f3b161d4740000811115612b995760405162461bcd60e51b8152600401610a849061492e565b612ba7607d607e5483613702565b607e55607c819055949350505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610da6908490613780565b6001600160a01b038216612c2f5760405162461bcd60e51b8152600401610a8490614dcb565b6000612c3a82612df3565b925050506000612c49856113b3565b90506000612c578684612e2d565b6001600160a01b03878116600090815260208690526040902080546001600160a01b0319169188169190911790559050612c9381868487613938565b846001600160a01b0316866001600160a01b03167fe8d51c8e11bd570db1734c8ec775785330e77007feed45c43b608ef33ff914bd86604051612cd691906147a5565b60405180910390a3505050505050565b6000612cf184610c9b565b905080821115612d135760405162461bcd60e51b8152600401610a8490614863565b612d1d8483613afb565b612d29846000846134b7565b6000612d3483613b14565b9050612d6a6001600160a01b037f00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff5168583612bb7565b60405160009033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612d9f9087906146ce565b60405180910390a3846001600160a01b03167fa7c0f0cac6bd4d18042007706c84a8abe823751cf289b69c01e83eef7b5915c7858386604051612de493929190614666565b60405180910390a25050505050565b6000808080846001811115612e0457fe5b1415612e1a5750606b9150606c9050606d612e26565b50606e9150606f905060705b9193909250565b6001600160a01b03808316600090815260208390526040812054909116806118cf5783915050610b5a565b600190565b670de0b6b3a7640000607c55565b612e83600080516020614fe583398151915233613b22565b612e9b60008051602061509983398151915233613b22565b612eb3600080516020614fa583398151915233613b22565b612ecb60008051602061503083398151915233613b22565b612ee3600080516020614fe583398151915280613b2c565b612f09600080516020615099833981519152600080516020614fe5833981519152613b2c565b612f2f600080516020614fa5833981519152600080516020614fe5833981519152613b2c565b612f55600080516020615030833981519152600080516020614fe5833981519152613b2c565b612f8d7f36dc7495d0ae0bc2a620bf292049e4d4e5f800043895b13c08a1977d3a3297f5600080516020614fe5833981519152613b2c565b612fb3600080516020614fc5833981519152600080516020614fe5833981519152613b2c565b565b814210612fd45760405162461bcd60e51b8152600401610a8490614d35565b612fdd816131f4565b610da6838361236d565b613019613014427f000000000000000000000000000000000000000000000000000000006138cff0613b76565b613b85565b6072601c6101000a81548163ffffffff021916908363ffffffff160217905550565b600080613046612420565b9050600061305585600161342e565b9050613062858284613bad565b50610baa8585613bc6565b60004383111561308f5760405162461bcd60e51b8152600401610a8490614a5e565b8361309b5750806113ab565b6000808052602086905260409020548310156130b85750806113ab565b600019840160009081526020869052604090205483106130ef575060001983016000908152602085905260409020600101546113ab565b600060001985015b8181111561316f57600282820304810361310f61430d565b50600081815260208981526040918290208251808401909352805480845260019091015491830191909152871415613150576020015193506113ab92505050565b805187111561316157819350613168565b6001820392505b50506130f7565b506000908152602086905260409020600101549050949350505050565b60007812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218311156131d75760006131c383670de0b6b3a7640000612ab7565b90506131cf8482612ab7565b915050610b5a565b6131ed826110d585670de0b6b3a76400006124e2565b9050610b5a565b60678190556040517fb94332f70bda7d9f80755fda0fee46f9fb73433eb08054c482d060b9732a5e37906132299083906146ce565b60405180910390a150565b61323d81613c7f565b5060718290556040517ffd301ea009c64d5832f2d8f8d8f632dda101449dd7bab7e219a7d4fe924f190a906132739084906146ce565b60405180910390a15050565b60008161ffff811681146109f95760405162461bcd60e51b8152600401610a8490614afd565b6060808260020260020167ffffffffffffffff811180156132c557600080fd5b506040519080825280601f01601f1916602001820160405280156132f0576020820181803683370190505b509050600360fc1b8160008151811061330557fe5b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061332e57fe5b60200101906001600160f81b031916908160001a905350600160028402015b60018111156133af576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061337857fe5b1a60f81b82828151811061338857fe5b60200101906001600160f81b031916908160001a90535060049490941c936000190161334d565b5083156118cf576040805162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015290519081900360640190fd5b6000816001600160801b03811681146109f95760405162461bcd60e51b8152600401610a84906148f7565b60008061343b8484613c8b565b90506134456142d6565b613450828686613cfe565b60208101519091506001600160f01b0316610baa8383613d70565b6000613475612420565b9050600061348585600185613dc0565b9050600061349585600186613e4b565b90506134a2868385613bad565b506134ae858285613bad565b50505050505050565b60006134c484606d612e2d565b905060006134d384606d612e2d565b90506134e28282856000613938565b60006134ef866070612e2d565b905060006134fe866070612e2d565b90506134ae8282876001613938565b6000818361355c5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561233257818101518382015260200161231a565b5082848161356657fe5b06949350505050565b607c546000907407ec3daf941806506c5e54eb70c4429fe3b6e5b0048111156135bb5760006135a682670de0b6b3a7640000612ab7565b90506135b284826124e2565b925050506109fc565b6135d1670de0b6b3a76400006110d585846124e2565b9150506109fc565b60006135e86000600184613e4b565b905060006135f884600185613e4b565b9050613605848284613bad565b5050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052613666908590613780565b50505050565b6136796000600183613eaf565b61368582600183613eaf565b61369160008083613f0d565b610ef682600083613f0d565b600081836136ec5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561233257818101518382015260200161231a565b5060008385816136f857fe5b0495945050505050565b60004383158015906137265750600019840160009081526020869052604090205481145b1561374c57505060001982016000908152602084905260409020600101819055816118cf565b60408051808201825291825260208083018581526000878152918890529190209151825551600191820155830190506118cf565b613792826001600160a01b0316613f53565b6137e3576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106138215780518252601f199092019160209182019101613802565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613883576040519150601f19603f3d011682016040523d82523d6000602084013e613888565b606091505b5091509150816138df576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613666578080602001905160208110156138fb57600080fd5b50516136665760405162461bcd60e51b815260040180806020018281038252602a8152602001806150e7602a913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b0316141561395757613666565b60008061396383612df3565b5090925090506001600160a01b03861615613a2e576001600160a01b03861660009081526020838152604080832091849052822054909181156139b9575060001981016000908152602083905260409020600101545b60006139c582896127f9565b90506139d2848483613702565b6001600160a01b038b16600081815260208890526040908190209290925590517fa0a19463ee116110c9b282012d9b65cc5522dc38a9520340cbaf3142e550127f90613a219084908b90614f54565b60405180910390a2505050505b6001600160a01b03851615613af3576001600160a01b0385166000908152602083815260408083209184905282205490918115613a7e575060001981016000908152602083905260409020600101545b6000613a8a828961253b565b9050613a97848483613702565b6001600160a01b038a16600081815260208890526040908190209290925590517fa0a19463ee116110c9b282012d9b65cc5522dc38a9520340cbaf3142e550127f90613ae69084908b90614f54565b60405180910390a2505050505b505050505050565b613b0760008083613dc0565b50610da682600083613dc0565b60006109f982607c5461318c565b610ef6828261290d565b80613b3683610c86565b60405184907fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff90600090a460009182526020829052604090912060010155565b6000818311612b0857816118cf565b60008163ffffffff811681146109f95760405162461bcd60e51b8152600401610a8490614c32565b600080613bb983613c7f565b9050610baa858583613f8c565b6001600160a01b0380831660009081526074602052604081208054908290559091613c35907f00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff5167f000000000000000000000000639192d54431f8c816368d3fb4107bc168d0e871858461360c565b836001600160a01b03167f2ef606d064225d24c1514dc94907c134faee1237445c2f63f410cce0852b20548483604051613c70929190614687565b60405180910390a29392505050565b60006109f9824261407f565b60008115613cc8576001600160a01b03831615613cc057506001600160a01b0382166000908152607660205260409020610b5a565b506077610b5a565b6001600160a01b03831615613cf557506001600160a01b0382166000908152607960205260409020610b5a565b50607a92915050565b613d066142d6565b8115613d6757613d146142d6565b6000806000613d2288612108565b93509350935093508015613d5b576001600160a01b038716613d4e57613d48828461422c565b50613d5b565b613d59878385614266565b505b839450505050506118cf565b6113ab846126bc565b80518254602083015161ffff1990911661ffff92831617909116620100006001600160f01b0392831602178355604090910151600190920180546001600160f01b03191692909116919091179055565b600080613dcd8585613c8b565b9050613dd76142d6565b613de2828787613cfe565b60208101519091506001600160f01b0316613e05613e0082876127f9565b614280565b6001600160f01b0390811660208401526040830151613e2991613e009116876127f9565b6001600160f01b03166040830152613e418383613d70565b9695505050505050565b600080613e588585613c8b565b9050613e626142d6565b613e6d828787613cfe565b60208101519091506001600160f01b0316613e8b613e00828761253b565b6001600160f01b0390811660208401526040830151613e2991613e0091168761253b565b6000613ebb8484613c8b565b9050613ec56142d6565b613ed0828686613cfe565b9050613ef5613e008483604001516001600160f01b03166127f990919063ffffffff16565b6001600160f01b031660408201526136058282613d70565b6000613f198484613c8b565b9050613f236142d6565b613f2e828686613cfe565b9050613ef5613e008483604001516001600160f01b031661253b90919063ffffffff16565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906113ab575050151592915050565b6001600160a01b038316600090815260746020908152604080832054607390925282205483811415613fc0575090506118cf565b600085613fce575081614020565b6000613fda86846127f9565b90506000613ff4670de0b6b3a76400006110d58a856124e2565b9050614000858261253b565b6001600160a01b038a166000908152607460205260409020819055925050505b6001600160a01b03871660008181526073602052604090819020879055517ff2c02e23a652c66023e40b9cf4d657ebb15f9235c261a02f740a9fd7a0e5bed29061406d9088908590614f68565b60405180910390a29695505050505050565b6072546000906001600160e01b03811690600160e01b900463ffffffff16826140c8857f000000000000000000000000000000000000000000000000000000006a9ed16e612af9565b90508181116140dc57829350505050610b5a565b6071548015806140ea575086155b1561415b576140f882613b85565b6072601c6101000a81548163ffffffff021916908363ffffffff1602179055507fb9b54fb40571ef7044b07522f579f84f94c6a561ca45129676901ff7781f6d0d8460405161414791906146ce565b60405180910390a183945050505050610b5a565b600061416783856127f9565b90506000614185896110d5670de0b6b3a7640000612b4186886124e2565b90506000614193878361253b565b905061419e85613b85565b6072601c6101000a81548163ffffffff021916908363ffffffff1602179055506141c7816142ab565b607280546001600160e01b0319166001600160e01b03929092169190911790556040517fb9b54fb40571ef7044b07522f579f84f94c6a561ca45129676901ff7781f6d0d906142179083906146ce565b60405180910390a19998505050505050505050565b60008061423d61055b84600161253b565b9050600061424b858361407f565b60008581526075602052604090208190559250505092915050565b600081815260756020526040812054610baa858583613f8c565b6000816001600160f01b03811681146109f95760405162461bcd60e51b8152600401610a8490614ead565b6000816001600160e01b03811681146109f95760405162461bcd60e51b8152600401610a8490614a27565b604080516060810182526000808252602082018190529181019190915290565b604080518082019091526000808252602082015290565b604051806040016040528060008152602001600081525090565b80356001600160a01b03811681146109fc57600080fd5b8035600281106109fc57600080fd5b803560ff811681146109fc57600080fd5b60006020828403121561436f578081fd5b6118cf82614327565b6000806040838503121561438a578081fd5b61439383614327565b91506143a160208401614327565b90509250929050565b6000806000606084860312156143be578081fd5b6143c784614327565b92506143d560208501614327565b9150604084013590509250925092565b600080600080600080600060e0888a0312156143ff578283fd5b61440888614327565b965061441660208901614327565b955060408801359450606088013593506144326080890161434d565b925060a0880135915060c0880135905092959891949750929550565b60008060408385031215614460578182fd5b61446983614327565b91506143a16020840161433e565b600080600080600080600060e0888a031215614491578283fd5b61449a88614327565b96506144166020890161433e565b600080604083850312156144ba578182fd5b6144c383614327565b946020939093013593505050565b6000806000606084860312156144e5578283fd5b6144ee84614327565b9250602084013591506145036040850161433e565b90509250925092565b60008060008060008060c08789031215614524578182fd5b61452d87614327565b955060208701359450604087013593506145496060880161434d565b92506080870135915060a087013590509295509295509295565b600060208284031215614574578081fd5b5035919050565b6000806040838503121561458d578182fd5b823591506143a160208401614327565b6000602082840312156145ae578081fd5b81356001600160e01b0319811681146118cf578182fd5b6000602082840312156145d6578081fd5b5051919050565b600080604083850312156145ef578182fd5b50508035926020909101359150565b600080600060608486031215614612578081fd5b505081359360208301359350604090920135919050565b6002811061463357fe5b9052565b61190160f01b81526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b901515815260200190565b90815260200190565b9586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b9384526001600160a01b039290921660208401526040830152606082015260800190565b9485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b93845260ff9290921660208401526040830152606082015260800190565b60208101610b5a8284614629565b6000602080835283518082850152825b818110156147df578581018301518582016040015282016147c3565b818111156147f05783604083870101525b50601f01601f1916929092016040019392505050565b60208082526038908201527f534d315374616b696e673a20576974686472617720726571756573742065786360408201527f65656473206e657874206163746976652062616c616e63650000000000000000606082015260800190565b6020808252603b908201527f534d315374616b696e673a20576974686472617720616d6f756e74206578636560408201527f656473207374616b657220696e6163746976652062616c616e63650000000000606082015260800190565b6020808252601c908201527f534d3141646d696e3a20537461727465642065706f6368207a65726f00000000604082015260600190565b6020808252601c908201527f53616665436173743a20746f55696e74313238206f766572666c6f7700000000604082015260600190565b6020808252602b908201527f534d3145786368616e6765526174653a204d61782065786368616e676520726160408201526a1d1948195e18d95959195960aa1b606082015260800190565b6020808252601b908201527f534d3145524332303a20494e56414c49445f5349474e41545552450000000000604082015260600190565b6020808252818101527f534d3145524332303a205472616e7366657220746f2061646472657373283029604082015260600190565b60208082526022908201527f534d3145524332303a205472616e736665722066726f6d206164647265737328604082015261302960f01b606082015260800190565b6020808252601c908201527f53616665436173743a20746f55696e74323234206f766572666c6f7700000000604082015260600190565b60208082526022908201527f534d31536e617073686f74733a20494e56414c49445f424c4f434b5f4e554d4260408201526122a960f11b606082015260800190565b6020808252603f908201527f534d315374616b696e673a20576974686472617720726571756573747320726560408201527f737472696374656420696e2074686520626c61636b6f75742077696e646f7700606082015260800190565b6020808252601b908201527f53616665436173743a20746f55696e743136206f766572666c6f770000000000604082015260600190565b6020808252602c908201527f534d3145706f63685363686564756c653a2045706f6368207a65726f2068617360408201526b081b9bdd081cdd185c9d195960a21b606082015260800190565b6020808252602f90820152600080516020614f8583398151915260408201526e56414c49445f5349474e415455524560881b606082015260800190565b6020808252601c908201527f534d3145524332303a20494e56414c49445f45585049524154494f4e00000000604082015260600190565b6020808252603090820152600080516020614f8583398151915260408201526f2b20a624a22fa2ac2824a920aa24a7a760811b606082015260800190565b6020808252601b908201527f53616665436173743a20746f55696e743332206f766572666c6f770000000000604082015260600190565b6020808252601f908201527f534d3145524332303a20417070726f766520746f206164647265737328302900604082015260600190565b60208082526034908201527f534d3145524332303a205472616e736665722065786365656473206e6578742060408201527365706f6368206163746976652062616c616e636560601b606082015260800190565b60208082526021908201527f534d3145524332303a20417070726f76652066726f6d206164647265737328306040820152602960f81b606082015260800190565b6020808252603c908201527f534d3145706f63685363686564756c653a2045706f6368207a65726f206d757360408201527f7420737461727420616674657220696e697469616c697a6174696f6e00000000606082015260800190565b6020808252602b90820152600080516020614f8583398151915260408201526a56414c49445f4e4f4e434560a81b606082015260800190565b6020808252602f90820152600080516020614f8583398151915260408201526e56414c49445f44454c45474154454560881b606082015260800190565b60208082526018908201527f534d3141646d696e3a204368616e6765642065706f6368730000000000000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526017908201527f534d3145524332303a20494e56414c49445f4f574e4552000000000000000000604082015260600190565b6020808252601c908201527f53616665436173743a20746f55696e74323430206f766572666c6f7700000000604082015260600190565b81516001600160801b039081168252602092830151169181019190915260400190565b815181526020918201519181019190915260400190565b9182526001600160a01b0316602082015260400190565b9283526001600160a01b03919091166020830152604082015260600190565b828152604081016118cf6020830184614629565b918252602082015260400190565b60ff9190911681526020019056fe534d31476f7665726e616e6365506f77657244656c65676174696f6e3a20494ea69ba352872fe0ee634bc8d48d2a09a61267da1bfb2015e67a11ad05fe21f04ba6fbd0d4ef0ac50b4de984ab8f303863596293cce6d67dd6111979bcf56abe74b19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e534d3145524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636574ec845281a5bcabeef9a800a79d30928ff9e6f2dc6f69a233fc39a83cb81ed2536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77534d3145524332303a2044656372656173656420616c6c6f77616e63652062656c6f77207a65726f12b42e8a160f6064dc959c6f251e3af0750ad213dbecf573b4710d67d6c28e39436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a65645361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a2646970667358221220be2c7ace03c1f2297e54cd4c228002a6bdb85a5a2ae8483703ae308941ef9c6e64736f6c63430007050033

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

00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff500000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff5000000000000000000000000639192d54431f8c816368d3fb4107bc168d0e871000000000000000000000000000000000000000000000000000000006138cff0000000000000000000000000000000000000000000000000000000006a9ed16e

-----Decoded View---------------
Arg [0] : stakedToken (address): 0x92D6C1e31e14520e676a687F0a93788B716BEff5
Arg [1] : rewardsToken (address): 0x92D6C1e31e14520e676a687F0a93788B716BEff5
Arg [2] : rewardsTreasury (address): 0x639192D54431F8c816368D3FB4107Bc168d0E871
Arg [3] : distributionStart (uint256): 1631113200
Arg [4] : distributionEnd (uint256): 1788793198

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff5
Arg [1] : 00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff5
Arg [2] : 000000000000000000000000639192d54431f8c816368d3fb4107bc168d0e871
Arg [3] : 000000000000000000000000000000000000000000000000000000006138cff0
Arg [4] : 000000000000000000000000000000000000000000000000000000006a9ed16e


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

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