// File: BurnableToken.sol



pragma solidity ^0.7.0;
pragma experimental SMTChecker;

//SPDX-License-Identifier: MIT

import "ERC20Basic.sol";

/// @title BurnableToken
contract BurnableToken is ERC20Basic {
    using SafeMath for uint256;

    event Burn(address indexed burner, uint256 value);

    function burn(uint256 _value) public returns (bool){
        address _who = msg.sender;
        require(_value <= balances[_who]);

        balances[_who] = balances[_who].sub(_value);
        totalSupply_ = totalSupply_.sub(_value);
        emit Burn(_who, _value);
        emit Transfer(_who, address(0), _value);

        return true;
    }
}

// File: CanReclaimToken.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental SMTChecker;


import "ERC20If.sol";
import "OwnableIf.sol";

/// @title CanReclaimToken
abstract contract CanReclaimToken is OwnableIf {

    function reclaimToken(ERC20If _token) external onlyOwner {
        uint256 balance = _token.balanceOf((address)(this));
        require(_token.transfer(_owner(), balance));
    }

}


// File: Claimable.sol



//SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;
pragma experimental SMTChecker;


import "Ownable.sol";

// File: openzeppelin-solidity/contracts/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, "no permission");
        _;
    }

    /**
     * @dev Allows the current owner to set the pendingOwner address.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) override 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: ERC20Basic.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental SMTChecker;


import "ERC20If.sol";
import "SafeMathLib.sol";

/// @title ERC20Basic
contract ERC20Basic is ERC20If {
    using SafeMath for uint256;

    mapping(address => uint256) internal balances;

    mapping(address => mapping(address => uint256)) internal allowed;

    uint256 internal totalSupply_;

    function _notPaused() virtual internal view returns (bool){return false;}

    function _notBlocked(address) virtual internal view returns (bool){return false;}

    modifier notPaused() {
        require(_notPaused(), "contract has been paused");
        _;
    }

    modifier notBlocked() {
        require(_notBlocked(msg.sender), "sender has been blocked");
        _;
    }

    function totalSupply() override public view returns (uint256) {
        return totalSupply_;
    }

    function transfer(address _to, uint256 _value) override public notPaused notBlocked returns (bool) {
        require(_notBlocked(_to), "to-address has been blocked");
        require(_value <= balances[msg.sender], "insufficient balance");
        require(_to != address(0), "invalid to-address");

        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        emit Transfer(msg.sender, _to, _value);
        return true;
    }

    function balanceOf(address _owner) override public view returns (uint256) {
        return balances[_owner];
    }

    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    )
    override public notPaused notBlocked
    returns (bool)
    {
        require(_notBlocked(_from), "from-address has been blocked");
        require(_notBlocked(_to), "to-address has been blocked");
        require(_value <= balances[_from], "insufficient balance");
        require(_to != address(0), "invalid to-address");
        if (_from == msg.sender){
            balances[_from] = balances[_from].sub(_value);
            balances[_to] = balances[_to].add(_value);
            return true;
        }

        require(_value <= allowed[_from][msg.sender], "value > allowed");


        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        emit Transfer(_from, _to, _value);
        return true;
    }

    function approve(address _spender, uint256 _value)
    override public notPaused notBlocked
    returns (bool) {
        require(_notBlocked(_spender), "spender-address has been blocked");
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    function increaseApproval(
        address _spender,
        uint256 _addedValue
    )
    public notPaused notBlocked
    returns (bool)
    {
        require(_notBlocked(_spender), "spender-address has been blocked");
        allowed[msg.sender][_spender] = (
        allowed[msg.sender][_spender].add(_addedValue));
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    function decreaseApproval(
        address _spender,
        uint _subtractedValue
    )
    public
    notPaused notBlocked
    returns (bool success)
    {
        require(_notBlocked(_spender), "spender-address has been blocked");

        uint256 oldValue = allowed[msg.sender][_spender];
        if (_subtractedValue >= oldValue) {
            allowed[msg.sender][_spender] = 0;
        } else {
            allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
        }
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    function allowance(
        address _owner,
        address _spender
    )
    override
    public
    view
    returns (uint256)
    {
        return allowed[_owner][_spender];
    }
}

// File: ERC20If.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental SMTChecker;

/// @title ERC20If
abstract contract ERC20If {
    function totalSupply() virtual public view returns (uint256);

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

    function transfer(address _to, uint256 _value) virtual public returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);

    function allowance(address _owner, address _spender) virtual public view returns (uint256);

    function transferFrom(address _from, address _to, uint256 _value) virtual public returns (bool);

    function approve(address _spender, uint256 _value) virtual public returns (bool);

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}


// File: MBTC.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental SMTChecker;

import "MToken.sol";

/// @title MBTC
contract MBTC is MToken {
    constructor() MToken("Matrix BTC Token", "MBTC", 8, (ERC20ControllerViewIf)(0)){}
}

// File: MemberMgrIf.sol



pragma solidity ^0.7.0;
pragma experimental SMTChecker;

//SPDX-License-Identifier: MIT
/// @title MemberMgrIf
abstract contract MemberMgrIf {
    function requireMerchant(address _who) virtual public view;

    function requireCustodian(address _who) virtual public view;
}

// File: MintableERC20.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental SMTChecker;


import "ERC20Basic.sol";
import "OwnableIf.sol";

/// @title MintableERC20
abstract contract MintableERC20 is ERC20Basic, OwnableIf {
    using SafeMath for uint256;

    event Mint(address indexed to, uint256 amount);
    event MintFinished(bool indexed finished);

    bool public mintingFinished = false;

    modifier canMint() {
        require(!mintingFinished, "can't mint");
        _;
    }

    modifier hasMintPermission() {
        require(msg.sender == _owner(), "no permission...");
        _;
    }

    /**
     * @dev Function to mint tokens
     * @param _to The address that will receive the minted tokens.
     * @param _amount The amount of tokens to mint.
     * @return A boolean that indicates if the operation was successful.
     */
    function mint(
        address _to,
        uint256 _amount
    )
    public
    hasMintPermission
    canMint
    notPaused
    returns (bool)
    {
        require(_notBlocked(_to), "to-address has been blocked");
        totalSupply_ = totalSupply_.add(_amount);
        balances[_to] = balances[_to].add(_amount);
        emit Mint(_to, _amount);
        emit Transfer(address(0), _to, _amount);
        return true;
    }

    /**
     * @dev Function to stop minting new tokens.
     * @return True if the operation was successful.
     */
    function finishMinting(bool finished)
    public
    onlyOwner
    returns (bool) {
        mintingFinished = finished;
        emit MintFinished(mintingFinished);
        return true;
    }
}

// File: MToken.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental SMTChecker;


import "MTokenControllerIf.sol";
import "NamedERC20.sol";
import "MintableERC20.sol";
import "BurnableToken.sol";
import "Claimable.sol";
import "CanReclaimToken.sol";

/// @title MToken
contract MToken is NamedERC20, Claimable, MintableERC20, BurnableToken, CanReclaimToken {
    using SafeMath for uint256;

    ERC20ControllerViewIf public erc20Controller;
    constructor(string memory _name,
        string memory _symbol,
        uint8 _decimals,
        ERC20ControllerViewIf _erc20Controller
    ) NamedERC20(_name, _symbol, _decimals){
        erc20Controller = _erc20Controller;
    }

    function hasController() view public returns (bool){
        return (address)(erc20Controller) != (address)(0);
    }

    function _notPaused() override internal view returns (bool){
        if (hasController()) {
            return !erc20Controller.paused();
        }
        return true;
    }

    function _notBlocked(address _who) override internal view returns (bool){
        if (hasController()) {
            return !erc20Controller.blocked(_who);
        }
        return true;
    }

    function setController(ERC20ControllerViewIf newController) public onlyOwner {
        erc20Controller = newController;
    }

    event BurnBlocked(address indexed burner, uint256 value);

    function burnBlocked(address addrBlocked, uint256 amount) public onlyOwner returns (bool){
        address _who = addrBlocked;
        require(!_notBlocked(_who), "addr not blocked");

        uint256 _value = amount;
        if (_value > balances[_who]) {
            _value = balances[_who];
        }

        balances[_who] = balances[_who].sub(_value);
        totalSupply_ = totalSupply_.sub(_value);
        emit BurnBlocked(_who, _value);
        emit Transfer(_who, address(0), _value);

        return true;
    }

}

// File: MTokenControllerIf.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental SMTChecker;


import "MemberMgrIf.sol";
import "ERC20If.sol";
/// @title ERC20ControllerViewIf
abstract contract ERC20ControllerViewIf {
    function blocked(address _who) virtual public view returns (bool);

    function paused() virtual public view returns (bool);
}

/// @title MTokenControllerIf
abstract contract MTokenControllerIf is MemberMgrIf, ERC20ControllerViewIf {
    function mint(address to, uint amount) virtual external returns (bool);

    function burn(uint value) virtual external returns (bool);

    function getMToken() virtual external returns (ERC20If);
}

// File: NamedERC20.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental SMTChecker;

/// @title NamedERC20
contract NamedERC20 {
    string public name;
    string public symbol;
    uint8 public decimals;

    constructor(string memory _name, string memory _symbol, uint8 _decimals) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
    }
}

// File: Ownable.sol



//SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;
pragma experimental SMTChecker;


import "OwnableIf.sol";

// File: openzeppelin-solidity/contracts/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 is OwnableIf {
    address public owner;

    function _owner() view override public returns (address){
        return 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() {
        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) virtual 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), "invalid _newOwner");
        emit OwnershipTransferred(owner, _newOwner);
        owner = _newOwner;
    }
}



// File: OwnableIf.sol



//SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;
pragma experimental SMTChecker;

// File: openzeppelin-solidity/contracts/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".
 */
abstract contract OwnableIf {

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == _owner(), "not owner......");
        _;
    }

    function _owner() view virtual public returns (address);
}

// File: SafeMathLib.sol




// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;
pragma experimental SMTChecker;

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