pragma solidity 0.4.25;

// File: contracts/ERC777/ERC20Token.sol

/* This Source Code Form is subject to the terms of the Mozilla external
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This code has not been reviewed.
 * Do not use or deploy this code before reviewing it personally first.
 */


interface ERC20Token {
  function name() external view returns (string);
  function symbol() external view returns (string);
  function decimals() external view returns (uint8);
  function totalSupply() external view returns (uint256);
  function balanceOf(address owner) external view returns (uint256);
  function transfer(address to, uint256 amount) external returns (bool);
  function transferFrom(address from, address to, uint256 amount) external returns (bool);
  function approve(address spender, uint256 amount) external returns (bool);
  function allowance(address owner, address spender) external view returns (uint256);

  event Transfer(address indexed from, address indexed to, uint256 amount);
  event Approval(address indexed owner, address indexed spender, uint256 amount);
}

// File: contracts/ERC820/ERC820Client.sol

contract ERC820Registry {
    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external;
    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address);
    function setManager(address _addr, address _newManager) external;
    function getManager(address _addr) public view returns(address);
}


/// Base client to interact with the registry.
contract ERC820Client {
    ERC820Registry erc820Registry = ERC820Registry(0x820c4597Fc3E4193282576750Ea4fcfe34DdF0a7);

    function setInterfaceImplementation(string _interfaceLabel, address _implementation) internal {
        bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel));
        erc820Registry.setInterfaceImplementer(this, interfaceHash, _implementation);
    }

    function interfaceAddr(address addr, string _interfaceLabel) internal view returns(address) {
        bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel));
        return erc820Registry.getInterfaceImplementer(addr, interfaceHash);
    }

    function delegateManagement(address _newManager) internal {
        erc820Registry.setManager(this, _newManager);
    }
}

// File: contracts/openzeppelin-solidity/math/SafeMath.sol

/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, reverts on 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-solidity/pull/522
    if (a == 0) {
      return 0;
    }

    uint256 c = a * b;
    require(c / a == b);

    return c;
  }

  /**
  * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b > 0); // Solidity only automatically asserts when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold

    return c;
  }

  /**
  * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b <= a);
    uint256 c = a - b;

    return c;
  }

  /**
  * @dev Adds two numbers, reverts on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a);

    return c;
  }

  /**
  * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
  * reverts when dividing by zero.
  */
  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b != 0);
    return a % b;
  }
}

// File: contracts/openzeppelin-solidity/Address.sol

/**
 * Utility library of inline functions on addresses
 */
library Address {

  /**
   * Returns whether the target address is a contract
   * @dev This function will return false if invoked during the constructor of a contract,
   * as the code is not actually created until after the constructor finishes.
   * @param account address of the account to check
   * @return whether the target address is a contract
   */
  function isContract(address account) internal view returns (bool) {
    uint256 size;
    // XXX Currently there is no better way to check if there is a contract in an address
    // than to check the size of the code at that address.
    // See https://ethereum.stackexchange.com/a/14016/36603
    // for more details about how this works.
    // TODO Check this again before the Serenity release, because all addresses will be
    // contracts then.
    // solium-disable-next-line security/no-inline-assembly
    assembly { size := extcodesize(account) }
    return size > 0;
  }

}

// File: contracts/ERC777/ERC777Token.sol

/* This Source Code Form is subject to the terms of the Mozilla external
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This code has not been reviewed.
 * Do not use or deploy this code before reviewing it personally first.
 */


interface ERC777Token {
  function name() external view returns (string);
  function symbol() external view returns (string);
  function totalSupply() external view returns (uint256);
  function balanceOf(address owner) external view returns (uint256);
  function granularity() external view returns (uint256);

  function defaultOperators() external view returns (address[]);
  function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
  function authorizeOperator(address operator) external;
  function revokeOperator(address operator) external;

  function send(address to, uint256 amount, bytes holderData) external;
  function operatorSend(address from, address to, uint256 amount, bytes holderData, bytes operatorData) external;

  function burn(uint256 amount, bytes holderData) external;
  function operatorBurn(address from, uint256 amount, bytes holderData, bytes operatorData) external;

  event Sent(
    address indexed operator,
    address indexed from,
    address indexed to,
    uint256 amount,
    bytes holderData,
    bytes operatorData
  );
  event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData);
  event Burned(address indexed operator, address indexed from, uint256 amount, bytes holderData, bytes operatorData);
  event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
  event RevokedOperator(address indexed operator, address indexed tokenHolder);
}

// File: contracts/ERC777/ERC777TokensSender.sol

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This code has not been reviewed.
 * Do not use or deploy this code before reviewing it personally first.
 */


interface ERC777TokensSender {
  function tokensToSend(
    address operator,
    address from,
    address to,
    uint amount,
    bytes userData,
    bytes operatorData
  ) external;
}

// File: contracts/ERC777/ERC777TokensRecipient.sol

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This code has not been reviewed.
 * Do not use or deploy this code before reviewing it personally first.
 */


interface ERC777TokensRecipient {
  function tokensReceived(
    address operator,
    address from,
    address to,
    uint amount,
    bytes userData,
    bytes operatorData
  ) external;
}

// File: contracts/ERC777/ERC777BaseToken.sol

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */








contract ERC777BaseToken is ERC777Token, ERC820Client {
  using SafeMath for uint256;
  using Address for address;

  string internal mName;
  string internal mSymbol;
  uint256 internal mGranularity;
  uint256 internal mTotalSupply;


  mapping(address => uint) internal mBalances;
  mapping(address => mapping(address => bool)) internal mAuthorized;

  address[] internal mDefaultOperators;
  mapping(address => bool) internal mIsDefaultOperator;
  mapping(address => mapping(address => bool)) internal mRevokedDefaultOperator;

  /* -- Constructor -- */
  //
  /// @notice Constructor to create a SelfToken
  /// @param _name Name of the new token
  /// @param _symbol Symbol of the new token.
  /// @param _granularity Minimum transferable chunk.
  constructor(
    string _name,
    string _symbol,
    uint256 _granularity,
    address[] _defaultOperators
  )
    internal
  {
    mName = _name;
    mSymbol = _symbol;
    mTotalSupply = 0;
    require(_granularity >= 1);
    mGranularity = _granularity;

    mDefaultOperators = _defaultOperators;
    for (uint i = 0; i < mDefaultOperators.length; i++) {
      mIsDefaultOperator[mDefaultOperators[i]] = true;
    }

    setInterfaceImplementation("ERC777Token", this);
  }

  /* -- ERC777 Interface Implementation -- */

  /// @notice Send `_amount` of tokens to address `_to` passing `_userData` to the recipient
  /// @param _to The address of the recipient
  /// @param _amount The number of tokens to be sent
  function send(address _to, uint256 _amount, bytes _userData) external {
    doSend(msg.sender, msg.sender, _to, _amount, _userData, "", true);
  }

  /// @notice Send `_amount` of tokens on behalf of the address `from` to the address `to`.
  /// @param _from The address holding the tokens being sent
  /// @param _to The address of the recipient
  /// @param _amount The number of tokens to be sent
  /// @param _userData Data generated by the user to be sent to the recipient
  /// @param _operatorData Data generated by the operator to be sent to the recipient
  function operatorSend(address _from, address _to, uint256 _amount, bytes _userData, bytes _operatorData) external {
    require(isOperatorFor(msg.sender, _from));
    doSend(msg.sender, _from, _to, _amount, _userData, _operatorData, true);
  }

  function burn(uint256 _amount, bytes _holderData) external {
    doBurn(msg.sender, msg.sender, _amount, _holderData, "");
  }

  function operatorBurn(address _tokenHolder, uint256 _amount, bytes _holderData, bytes _operatorData) external {
    require(isOperatorFor(msg.sender, _tokenHolder));
    doBurn(msg.sender, _tokenHolder, _amount, _holderData, _operatorData);
  }

  /// @return the name of the token
  function name() external view returns (string) { return mName; }

  /// @return the symbol of the token
  function symbol() external view returns (string) { return mSymbol; }

  /// @return the granularity of the token
  function granularity() external view returns (uint256) { return mGranularity; }

  /// @return the total supply of the token
  function totalSupply() public view returns (uint256) { return mTotalSupply; }

  /// @notice Return the account balance of some account
  /// @param _tokenHolder Address for which the balance is returned
  /// @return the balance of `_tokenAddress`.
  function balanceOf(address _tokenHolder) public view returns (uint256) { return mBalances[_tokenHolder]; }

  /// @notice Return the list of default operators
  /// @return the list of all the default operators
  function defaultOperators() external view returns (address[]) { return mDefaultOperators; }

  /// @notice Authorize a third party `_operator` to manage (send) `msg.sender`'s tokens. An operator cannot be reauthorized
  /// @param _operator The operator that wants to be Authorized
  function authorizeOperator(address _operator) external {
    require(_operator != msg.sender);
    require(!mAuthorized[_operator][msg.sender]);

    if (mIsDefaultOperator[_operator]) {
      mRevokedDefaultOperator[_operator][msg.sender] = false;
    } else {
      mAuthorized[_operator][msg.sender] = true;
    }
    emit AuthorizedOperator(_operator, msg.sender);
  }

  /// @notice Revoke a third party `_operator`'s rights to manage (send) `msg.sender`'s tokens.
  /// @param _operator The operator that wants to be Revoked
  function revokeOperator(address _operator) external {
    require(_operator != msg.sender);
    require(mAuthorized[_operator][msg.sender]);

    if (mIsDefaultOperator[_operator]) {
      mRevokedDefaultOperator[_operator][msg.sender] = true;
    } else {
      mAuthorized[_operator][msg.sender] = false;
    }
    emit RevokedOperator(_operator, msg.sender);
  }

  /// @notice Check whether the `_operator` address is allowed to manage the tokens held by `_tokenHolder` address.
  /// @param _operator address to check if it has the right to manage the tokens
  /// @param _tokenHolder address which holds the tokens to be managed
  /// @return `true` if `_operator` is authorized for `_tokenHolder`
  function isOperatorFor(address _operator, address _tokenHolder) public view returns (bool) {
    return (
      _operator == _tokenHolder
      || mAuthorized[_operator][_tokenHolder]
      || (mIsDefaultOperator[_operator] && !mRevokedDefaultOperator[_operator][_tokenHolder])
    );
  }

  /* -- Helper Functions -- */
  //
  /// @notice Internal function that ensures `_amount` is multiple of the granularity
  /// @param _amount The quantity that want's to be checked
  function requireMultiple(uint256 _amount) internal view {
    require(_amount.div(mGranularity).mul(mGranularity) == _amount);
  }

  /// @notice Helper function actually performing the sending of tokens.
  /// @param _operator The address performing the send
  /// @param _from The address holding the tokens being sent
  /// @param _to The address of the recipient
  /// @param _amount The number of tokens to be sent
  /// @param _userData Data generated by the user to be passed to the recipient
  /// @param _operatorData Data generated by the operator to be passed to the recipient
  /// @param _preventLocking `true` if you want this function to throw when tokens are sent to a contract not
  ///  implementing `ERC777TokensRecipient`.
  ///  ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer
  ///  functions SHOULD set this parameter to `false`.
  function doSend(
    address _operator,
    address _from,
    address _to,
    uint256 _amount,
    bytes _userData,
    bytes _operatorData,
    bool _preventLocking
  )
    internal
  {
    requireMultiple(_amount);

    callSender(_operator, _from, _to, _amount, _userData, _operatorData);

    require(_to != address(0));          // forbid sending to 0x0 (=burning)
    require(mBalances[_from] >= _amount); // ensure enough funds

    mBalances[_from] = mBalances[_from].sub(_amount);
    mBalances[_to] = mBalances[_to].add(_amount);

    callRecipient(_operator, _from, _to, _amount, _userData, _operatorData, _preventLocking);

    emit Sent(_operator, _from, _to, _amount, _userData, _operatorData);
  }

  /// @notice Helper function actually performing the burning of tokens.
  /// @param _operator The address performing the burn
  /// @param _tokenHolder The address holding the tokens being burn
  /// @param _amount The number of tokens to be burnt
  /// @param _holderData Data generated by the token holder
  /// @param _operatorData Data generated by the operator
  function doBurn(address _operator, address _tokenHolder, uint256 _amount, bytes _holderData, bytes _operatorData)
    internal
  {
    requireMultiple(_amount);
    require(balanceOf(_tokenHolder) >= _amount);

    mBalances[_tokenHolder] = mBalances[_tokenHolder].sub(_amount);
    mTotalSupply = mTotalSupply.sub(_amount);

    callSender(_operator, _tokenHolder, 0x0, _amount, _holderData, _operatorData);
    emit Burned(_operator, _tokenHolder, _amount, _holderData, _operatorData);
  }

  /// @notice Helper function that checks for ERC777TokensRecipient on the recipient and calls it.
  ///  May throw according to `_preventLocking`
  /// @param _operator The address performing the send or mint
  /// @param _from The address holding the tokens being sent
  /// @param _to The address of the recipient
  /// @param _amount The number of tokens to be sent
  /// @param _userData Data generated by the user to be passed to the recipient
  /// @param _operatorData Data generated by the operator to be passed to the recipient
  /// @param _preventLocking `true` if you want this function to throw when tokens are sent to a contract not
  ///  implementing `ERC777TokensRecipient`.
  ///  ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer
  ///  functions SHOULD set this parameter to `false`.
  function callRecipient(
    address _operator,
    address _from,
    address _to,
    uint256 _amount,
    bytes _userData,
    bytes _operatorData,
    bool _preventLocking
  )
    internal
  {
    address recipientImplementation = interfaceAddr(_to, "ERC777TokensRecipient");
    if (recipientImplementation != 0) {
      ERC777TokensRecipient(recipientImplementation).tokensReceived(
        _operator, _from, _to, _amount, _userData, _operatorData);
    } else if (_preventLocking) {
      require(!_to.isContract());
    }
  }

  /// @notice Helper function that checks for ERC777TokensSender on the sender and calls it.
  ///  May throw according to `_preventLocking`
  /// @param _from The address holding the tokens being sent
  /// @param _to The address of the recipient
  /// @param _amount The amount of tokens to be sent
  /// @param _userData Data generated by the user to be passed to the recipient
  /// @param _operatorData Data generated by the operator to be passed to the recipient
  ///  implementing `ERC777TokensSender`.
  ///  ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer
  ///  functions SHOULD set this parameter to `false`.
  function callSender(
    address _operator,
    address _from,
    address _to,
    uint256 _amount,
    bytes _userData,
    bytes _operatorData
  )
    internal
  {
    address senderImplementation = interfaceAddr(_from, "ERC777TokensSender");
    if (senderImplementation == 0) {
      return;
    }
    ERC777TokensSender(senderImplementation).tokensToSend(_operator, _from, _to, _amount, _userData, _operatorData);
  }
}

// File: contracts/ERC777/ERC777ERC20BaseToken.sol

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */




contract ERC777ERC20BaseToken is ERC20Token, ERC777BaseToken {
  bool internal mErc20compatible;

  mapping(address => mapping(address => uint256)) internal mAllowed;

  constructor(
    string _name,
    string _symbol,
    uint256 _granularity,
    address[] _defaultOperators
  )
    internal ERC777BaseToken(_name, _symbol, _granularity, _defaultOperators)
  {
    mErc20compatible = true;
    setInterfaceImplementation("ERC20Token", this);
  }

  /// @notice This modifier is applied to erc20 obsolete methods that are
  ///  implemented only to maintain backwards compatibility. When the erc20
  ///  compatibility is disabled, this methods will fail.
  modifier erc20 () {
    require(mErc20compatible);
    _;
  }

  /// @notice For Backwards compatibility
  /// @return The decimls of the token. Forced to 18 in ERC777.
  function decimals() external erc20 view returns (uint8) { return uint8(18); }

  /// @notice ERC20 backwards compatible transfer.
  /// @param _to The address of the recipient
  /// @param _amount The number of tokens to be transferred
  /// @return `true`, if the transfer can't be done, it should fail.
  function transfer(address _to, uint256 _amount) public erc20 returns (bool success) {
    doSend(msg.sender, msg.sender, _to, _amount, "", "", false);
    return true;
  }

  /// @notice ERC20 backwards compatible transferFrom.
  /// @param _from The address holding the tokens being transferred
  /// @param _to The address of the recipient
  /// @param _amount The number of tokens to be transferred
  /// @return `true`, if the transfer can't be done, it should fail.
  function transferFrom(address _from, address _to, uint256 _amount) public erc20 returns (bool success) {
    require(_amount <= mAllowed[_from][msg.sender]);

    // Cannot be after doSend because of tokensReceived re-entry
    mAllowed[_from][msg.sender] = mAllowed[_from][msg.sender].sub(_amount);
    doSend(msg.sender, _from, _to, _amount, "", "", false);
    return true;
  }

  /// @notice ERC20 backwards compatible approve.
  ///  `msg.sender` approves `_spender` to spend `_amount` tokens on its behalf.
  /// @param _spender The address of the account able to transfer the tokens
  /// @param _amount The number of tokens to be approved for transfer
  /// @return `true`, if the approve can't be done, it should fail.
  function approve(address _spender, uint256 _amount) public erc20 returns (bool success) {
    mAllowed[msg.sender][_spender] = _amount;
    emit Approval(msg.sender, _spender, _amount);
    return true;
  }

  /// @notice ERC20 backwards compatible allowance.
  ///  This function makes it easy to read the `allowed[]` map
  /// @param _owner The address of the account that owns the token
  /// @param _spender The address of the account able to transfer the tokens
  /// @return Amount of remaining tokens of _owner that _spender is allowed
  ///  to spend
  function allowance(address _owner, address _spender) public erc20 view returns (uint256 remaining) {
    return mAllowed[_owner][_spender];
  }

  function doSend(
    address _operator,
    address _from,
    address _to,
    uint256 _amount,
    bytes _userData,
    bytes _operatorData,
    bool _preventLocking
  )
    internal
  {
    super.doSend(_operator, _from, _to, _amount, _userData, _operatorData, _preventLocking);
    if (mErc20compatible) {
      emit Transfer(_from, _to, _amount);
    }
  }

  function doBurn(address _operator, address _tokenHolder, uint256 _amount, bytes _holderData, bytes _operatorData)
    internal
  {
    super.doBurn(_operator, _tokenHolder, _amount, _holderData, _operatorData);
    if (mErc20compatible) {
      emit Transfer(_tokenHolder, 0x0, _amount);
    }
  }
}

// File: contracts/openzeppelin-solidity/ownership/Ownable.sol

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;


  event OwnershipRenounced(address indexed previousOwner);
  event OwnershipTransferred(
    address indexed previousOwner,
    address indexed newOwner
  );


  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  constructor() public {
    owner = msg.sender;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to relinquish control of the contract.
   * @notice Renouncing to ownership will leave the contract without an owner.
   * It will not be possible to call the functions with the `onlyOwner`
   * modifier anymore.
   */
  function renounceOwnership() public onlyOwner {
    emit OwnershipRenounced(owner);
    owner = address(0);
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
  function transferOwnership(address _newOwner) public onlyOwner {
    _transferOwnership(_newOwner);
  }

  /**
   * @dev Transfers control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
  function _transferOwnership(address _newOwner) internal {
    require(_newOwner != address(0));
    emit OwnershipTransferred(owner, _newOwner);
    owner = _newOwner;
  }
}

// File: contracts/openzeppelin-solidity/lifecycle/Pausable.sol

/**
 * @title Pausable
 * @dev Base contract which allows children to implement an emergency stop mechanism.
 */
contract Pausable is Ownable {
  event Pause();
  event Unpause();

  bool public paused = false;


  /**
   * @dev Modifier to make a function callable only when the contract is not paused.
   */
  modifier whenNotPaused() {
    require(!paused);
    _;
  }

  /**
   * @dev Modifier to make a function callable only when the contract is paused.
   */
  modifier whenPaused() {
    require(paused);
    _;
  }

  /**
   * @dev called by the owner to pause, triggers stopped state
   */
  function pause() public onlyOwner whenNotPaused {
    paused = true;
    emit Pause();
  }

  /**
   * @dev called by the owner to unpause, returns to normal state
   */
  function unpause() public onlyOwner whenPaused {
    paused = false;
    emit Unpause();
  }
}

// File: contracts/utils/Freezable.sol

/// @title An inheritable extension for a contract to freeze accessibility of any specific addresses
/// @author Jeff Hu
/// @notice Have a contract inherited from this to use the modifiers: whenAccountFrozen(), whenAccountNotFrozen()
/// @dev Concern: Ownable may cause multiple owners; You need to pass in msg.sender when using modifiers
contract Freezable is Ownable {

  event AccountFrozen(address indexed _account);
  event AccountUnfrozen(address indexed _account);

  // frozen status of all accounts
  mapping(address=>bool) public frozenAccounts;


   /**
   * @dev Modifier to make a function callable only when the address is frozen.
   */
  modifier whenAccountFrozen(address _account) {
    require(frozenAccounts[_account] == true);
    _;
  }

  /**
   * @dev Modifier to make a function callable only when the address is not frozen.
   */
  modifier whenAccountNotFrozen(address _account) {
    require(frozenAccounts[_account] == false);
    _;
  }


  /**
   * @dev Function to freeze an account from transactions
   */
  function freeze(address _account)
    external
    onlyOwner
    whenAccountNotFrozen(_account)
    returns (bool)
  {
    frozenAccounts[_account] = true;
    emit AccountFrozen(_account);
    return true;
  }

  /**
   * @dev Function to unfreeze an account form frozen state
   */
  function unfreeze(address _account)
    external
    onlyOwner
    whenAccountFrozen(_account)
    returns (bool)
  {
    frozenAccounts[_account] = false;
    emit AccountUnfrozen(_account);
    return true;
  }


  /**
   * @dev A user can choose to freeze her account (not unfreezable)
   */
  function freezeMyAccount()
    external
    whenAccountNotFrozen(msg.sender)
    returns (bool)
  {
    // require(msg.sender != owner);       // Only the owner cannot freeze herself

    frozenAccounts[msg.sender] = true;
    emit AccountFrozen(msg.sender);
    return true;
  }
}

// File: contracts/PausableFreezableERC777ERC20Token.sol

/// @dev The owner can pause/unpause the token.
/// When paused, all functions that may change the token balances are prohibited.
/// Function approve is prohibited too.
contract PausableFreezableERC777ERC20Token is ERC777ERC20BaseToken, Pausable, Freezable {

  // ERC777 methods

  /// @dev We can not call super.send() because send() is an external function.
  /// We can only override it.
  function send(address _to, uint256 _amount, bytes _userData)
    external
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_to)
  {
    doSend(msg.sender, msg.sender, _to, _amount, _userData, "", true);
  }

  function operatorSend(address _from, address _to, uint256 _amount, bytes _userData, bytes _operatorData)
    external
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_from)
    whenAccountNotFrozen(_to)
  {
    require(isOperatorFor(msg.sender, _from));
    doSend(msg.sender, _from, _to, _amount, _userData, _operatorData, true);
  }

  function burn(uint256 _amount, bytes _holderData)
    external
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
  {
    doBurn(msg.sender, msg.sender, _amount, _holderData, "");
  }

  function operatorBurn(address _tokenHolder, uint256 _amount, bytes _holderData, bytes _operatorData)
    external
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_tokenHolder)
  {
    require(isOperatorFor(msg.sender, _tokenHolder));
    doBurn(msg.sender, _tokenHolder, _amount, _holderData, _operatorData);
  }

  // ERC20 methods

  function transfer(address _to, uint256 _amount)
    public
    erc20
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_to)
    returns (bool success)
  {
    return super.transfer(_to, _amount);
  }

  function transferFrom(address _from, address _to, uint256 _amount)
    public
    erc20
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_from)
    whenAccountNotFrozen(_to)
    returns (bool success)
  {
    return super.transferFrom(_from, _to, _amount);
  }

  function approve(address _spender, uint256 _amount)
    public
    erc20
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_spender)
    returns (bool success)
  {
    return super.approve(_spender, _amount);
  }

  /// @dev allow Owner to transfer funds from a Frozen account
  /// @notice the "_from" account must be frozen
  /// @notice only the owner can trigger this function
  /// @notice super.doSend to skip "_from" frozen checking
  function transferFromFrozenAccount(
    address _from,
    address _to,
    uint256 _amount
  )
    external
    onlyOwner
    whenNotPaused
    whenAccountFrozen(_from)
    whenAccountNotFrozen(_to)
    whenAccountNotFrozen(msg.sender)
  {
    super.doSend(msg.sender, _from, _to, _amount, "", "", true);
  }

  function doSend(
    address _operator,
    address _from,
    address _to,
    uint256 _amount,
    bytes _userData,
    bytes _operatorData,
    bool _preventLocking
  )
    internal
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_operator)
    whenAccountNotFrozen(_from)
    whenAccountNotFrozen(_to)
  {
    super.doSend(_operator, _from, _to, _amount, _userData, _operatorData, _preventLocking);
  }

  function doBurn(address _operator, address _tokenHolder, uint256 _amount, bytes _holderData, bytes _operatorData)
    internal
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_operator)
    whenAccountNotFrozen(_tokenHolder)
  {
    super.doBurn(_operator, _tokenHolder, _amount, _holderData, _operatorData);
  }
}

// File: contracts/ERC777ERC20TokenWithOfficialOperators.sol

/// @title ERC777 ERC20 Token with Official Operators
/// @author Roger-Wu
/// @notice Official operators are officially recommended operator contracts.
/// By adding new official operators, we can keep adding new features to
/// an already deployed token contract, which can be viewed as a way to
/// upgrade the token contract.
/// Rules of official operators:
/// 1. An official operator must be a contract.
/// 2. An official operator can only be added or removed by the contract owner.
/// 3. A token holder can either accept all official operators or not.
///    By default, a token holder accepts all official operators, including
///    the official operators added in the future.
/// 4. If a token holder accepts all official operators, it works as if all
///    the addresses of official operators has been authorized to be his operator.
///    In this case, an official operator will always be the token holder's
///    operator even if he tries to revoke it by sending `revokeOperator` transactions.
/// 5. If a token holder chooses not to accept all official operators, it works as if
///    there is no official operator at all for him. The token holder can still authorize
///    any addresses, including which of official operators, to be his operators.
contract ERC777ERC20TokenWithOfficialOperators is ERC777ERC20BaseToken, Ownable {
  using Address for address;

  mapping(address => bool) internal mIsOfficialOperator;
  mapping(address => bool) internal mIsUserNotAcceptingAllOfficialOperators;

  event OfficialOperatorAdded(address operator);
  event OfficialOperatorRemoved(address operator);
  event OfficialOperatorsAcceptedByUser(address indexed user);
  event OfficialOperatorsRejectedByUser(address indexed user);

  /// @notice Add an address into the list of official operators.
  /// @param _operator The address of a new official operator.
  /// An official operator must be a contract.
  function addOfficialOperator(address _operator) external onlyOwner {
    require(_operator.isContract(), "An official operator must be a contract.");
    require(!mIsOfficialOperator[_operator], "_operator is already an official operator.");

    mIsOfficialOperator[_operator] = true;
    emit OfficialOperatorAdded(_operator);
  }

  /// @notice Delete an address from the list of official operators.
  /// @param _operator The address of an official operator.
  function removeOfficialOperator(address _operator) external onlyOwner {
    require(mIsOfficialOperator[_operator], "_operator is not an official operator.");

    mIsOfficialOperator[_operator] = false;
    emit OfficialOperatorRemoved(_operator);
  }

  /// @notice Unauthorize all official operators to manage `msg.sender`'s tokens.
  function rejectAllOfficialOperators() external {
    require(!mIsUserNotAcceptingAllOfficialOperators[msg.sender], "Official operators are already rejected by msg.sender.");

    mIsUserNotAcceptingAllOfficialOperators[msg.sender] = true;
    emit OfficialOperatorsRejectedByUser(msg.sender);
  }

  /// @notice Authorize all official operators to manage `msg.sender`'s tokens.
  function acceptAllOfficialOperators() external {
    require(mIsUserNotAcceptingAllOfficialOperators[msg.sender], "Official operators are already accepted by msg.sender.");

    mIsUserNotAcceptingAllOfficialOperators[msg.sender] = false;
    emit OfficialOperatorsAcceptedByUser(msg.sender);
  }

  /// @return true if the address is an official operator, false if not.
  function isOfficialOperator(address _operator) external view returns(bool) {
    return mIsOfficialOperator[_operator];
  }

  /// @return true if a user is accepting all official operators, false if not.
  function isUserAcceptingAllOfficialOperators(address _user) external view returns(bool) {
    return !mIsUserNotAcceptingAllOfficialOperators[_user];
  }

  /// @notice Check whether the `_operator` address is allowed to manage the tokens held by `_tokenHolder` address.
  /// @param _operator address to check if it has the right to manage the tokens
  /// @param _tokenHolder address which holds the tokens to be managed
  /// @return `true` if `_operator` is authorized for `_tokenHolder`
  function isOperatorFor(address _operator, address _tokenHolder) public view returns (bool) {
    return (
      _operator == _tokenHolder
      || (!mIsUserNotAcceptingAllOfficialOperators[_tokenHolder] && mIsOfficialOperator[_operator])
      || mAuthorized[_operator][_tokenHolder]
      || (mIsDefaultOperator[_operator] && !mRevokedDefaultOperator[_operator][_tokenHolder])
    );
  }
}

// File: contracts/ApprovalRecipient.sol

interface ApprovalRecipient {
  function receiveApproval(
    address _from,
    uint256 _value,
    address _token,
    bytes _extraData
  ) external;
}

// File: contracts/ERC777ERC20TokenWithApproveAndCall.sol

contract ERC777ERC20TokenWithApproveAndCall is PausableFreezableERC777ERC20Token {
  /// Set allowance for other address and notify
  /// Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it
  /// From https://www.ethereum.org/token
  /// @param _spender The address authorized to spend
  /// @param _value the max amount they can spend
  /// @param _extraData some extra information to send to the approved contract
  function approveAndCall(address _spender, uint256 _value, bytes _extraData)
    external
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_spender)
    returns (bool success)
  {
    ApprovalRecipient spender = ApprovalRecipient(_spender);
    if (approve(_spender, _value)) {
      spender.receiveApproval(msg.sender, _value, this, _extraData);
      return true;
    }
  }
}

// File: contracts/ERC777ERC20TokenWithBatchTransfer.sol

contract ERC777ERC20TokenWithBatchTransfer is PausableFreezableERC777ERC20Token {
  /// @notice ERC20 backwards compatible batch transfer.
  /// The transaction will revert if any of the recipients is frozen.
  /// We check whether a recipient is frozen in `doSend`.
  /// @param _recipients The addresses of the recipients
  /// @param _amounts The numbers of tokens to be transferred
  /// @return `true`, if the transfer can't be done, it should fail.
  function batchTransfer(address[] _recipients, uint256[] _amounts)
    external
    erc20
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    returns (bool success)
  {
    require(
      _recipients.length == _amounts.length,
      "The lengths of _recipients and _amounts should be the same."
    );

    for (uint256 i = 0; i < _recipients.length; i++) {
      doSend(msg.sender, msg.sender, _recipients[i], _amounts[i], "", "", false);
    }
    return true;
  }

  /// @notice Send tokens to multiple recipients.
  /// The transaction will revert if any of the recipients is frozen.
  /// We check whether a recipient is frozen in `doSend`.
  /// @param _recipients The addresses of the recipients
  /// @param _amounts The numbers of tokens to be transferred
  /// @param _userData Data generated by the user to be sent to the recipient
  function batchSend(
    address[] _recipients,
    uint256[] _amounts,
    bytes _userData
  )
    external
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
  {
    require(
      _recipients.length == _amounts.length,
      "The lengths of _recipients and _amounts should be the same."
    );

    for (uint256 i = 0; i < _recipients.length; i++) {
      doSend(msg.sender, msg.sender, _recipients[i], _amounts[i], _userData, "", true);
    }
  }

  /// @notice Send tokens to multiple recipients on behalf of the address `from`
  /// The transaction will revert if any of the recipients is frozen.
  /// We check whether a recipient is frozen in `doSend`.
  /// @param _from The address holding the tokens being sent
  /// @param _recipients The addresses of the recipients
  /// @param _amounts The numbers of tokens to be transferred
  /// @param _userData Data generated by the user to be sent to the recipient
  /// @param _operatorData Data generated by the operator to be sent to the recipient
  function operatorBatchSend(
    address _from,
    address[] _recipients,
    uint256[] _amounts,
    bytes _userData,
    bytes _operatorData
  )
    external
    whenNotPaused
    whenAccountNotFrozen(msg.sender)
    whenAccountNotFrozen(_from)
  {
    require(
      _recipients.length == _amounts.length,
      "The lengths of _recipients and _amounts should be the same."
    );
    require(isOperatorFor(msg.sender, _from));

    for (uint256 i = 0; i < _recipients.length; i++) {
      doSend(msg.sender, _from, _recipients[i], _amounts[i], _userData, _operatorData, true);
    }
  }
}

// File: contracts/CappedMintableERC777ERC20Token.sol

/// @title Capped Mintable ERC777 ERC20 Token
/// @author Roger-Wu
/// @dev Mintable token with a minting cap.
///  The owner can mint any amount of tokens until the cap is reached.
contract CappedMintableERC777ERC20Token is ERC777ERC20BaseToken, Ownable {
  uint256 internal mTotalSupplyCap;

  constructor(uint256 _totalSupplyCap) public {
    mTotalSupplyCap = _totalSupplyCap;
  }

  /// @return the cap of total supply
  function totalSupplyCap() external view returns(uint _totalSupplyCap) {
    return mTotalSupplyCap;
  }

  /// @dev Generates `_amount` tokens to be assigned to `_tokenHolder`
  ///  Sample mint function to showcase the use of the `Minted` event and the logic to notify the recipient.
  ///  Reference: https://github.com/jacquesd/ERC777/blob/devel/contracts/examples/SelfToken.sol
  /// @param _tokenHolder The address that will be assigned the new tokens
  /// @param _amount The quantity of tokens generated
  /// @param _operatorData Data that will be passed to the recipient as a first transfer
  function mint(address _tokenHolder, uint256 _amount, bytes _operatorData) external onlyOwner {
    requireMultiple(_amount);
    require(mTotalSupply.add(_amount) <= mTotalSupplyCap);

    mTotalSupply = mTotalSupply.add(_amount);
    mBalances[_tokenHolder] = mBalances[_tokenHolder].add(_amount);

    callRecipient(msg.sender, address(0), _tokenHolder, _amount, "", _operatorData, true);

    emit Minted(msg.sender, _tokenHolder, _amount, _operatorData);
    if (mErc20compatible) {
      emit Transfer(0x0, _tokenHolder, _amount);
    }
  }
}

// File: contracts/ERC777ERC20TokenWithOperatorApprove.sol

/// @title ERC777 ERC20 Token with Operator Approve
/// @author Roger-Wu
/// @notice Allow an operator to approve tokens for a token holder.
contract ERC777ERC20TokenWithOperatorApprove is ERC777ERC20BaseToken {
  function operatorApprove(
    address _tokenHolder,
    address _spender,
    uint256 _amount
  )
    external
    erc20
    returns (bool success)
  {
    require(
      isOperatorFor(msg.sender, _tokenHolder),
      "msg.sender is not an operator for _tokenHolder"
    );

    mAllowed[_tokenHolder][_spender] = _amount;
    emit Approval(_tokenHolder, _spender, _amount);
    return true;
  }
}

// File: contracts/openzeppelin-solidity/ownership/Claimable.sol

/**
 * @title Claimable
 * @dev Extension for the Ownable contract, where the ownership needs to be claimed.
 * This allows the new owner to accept the transfer.
 */
contract Claimable is Ownable {
  address public pendingOwner;

  /**
   * @dev Modifier throws if called by any account other than the pendingOwner.
   */
  modifier onlyPendingOwner() {
    require(msg.sender == pendingOwner);
    _;
  }

  /**
   * @dev Allows the current owner to set the pendingOwner address.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    pendingOwner = newOwner;
  }

  /**
   * @dev Allows the pendingOwner address to finalize the transfer.
   */
  function claimOwnership() public onlyPendingOwner {
    emit OwnershipTransferred(owner, pendingOwner);
    owner = pendingOwner;
    pendingOwner = address(0);
  }
}

// File: contracts/SelfToken.sol

/// @title SelfToken
/// @author Roger Wu (Roger-Wu), Tina Lee (tina1998612), Jeff Hu (yhuag)
/// @dev The inheritance order is important.
contract SelfToken is
  ERC777ERC20BaseToken,
  PausableFreezableERC777ERC20Token,
  ERC777ERC20TokenWithOfficialOperators,
  ERC777ERC20TokenWithApproveAndCall,
  ERC777ERC20TokenWithBatchTransfer,
  CappedMintableERC777ERC20Token,
  ERC777ERC20TokenWithOperatorApprove,
  Claimable
{
  constructor()
    public
    ERC777ERC20BaseToken("SELF TOKEN", "SELF", 1, new address[](0))
    CappedMintableERC777ERC20Token(1e9 * 1e18)
  {}
}