abstract contract ERC777MintablePausableBlocklistable is Context, AccessControl, ERC777, Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant BLOCK_ROLE = keccak256("BLOCK_ROLE");

    bool public _mintingFinished = false;

    mapping(address => bool) _blocklist;
    
    event Blocked(address account);
    event Unblocked(address account);
    event MintFinished();

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, and `PAUSER_ROLE` to the account that
     * deploys the contract.
     */
    constructor(        
        string memory name,
        string memory symbol,
        address[] memory defaultOperators
    ) 
        public ERC777(name, symbol, defaultOperators) 
    {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(BLOCK_ROLE, _msgSender());
    }

    /**
     * @dev Pauses all token transfers.
     *
     * See {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function pause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC777MintablePausableBlacklistable: must have pauser role to pause");
        _pause();
    }

    /**
     * @dev Unpauses all token transfers.
     *
     * See {Pausable-_unpause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function unpause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC777MintablePausableBlocklistable: must have pauser role to unpause");
        _unpause();
    }

    /**
     * @dev Block account
     *
     * Requirements:
     *
     * - the caller must have the `BLOCK_ROLE`.
     */
    function blockAccount(address account) public virtual {
        require(hasRole(BLOCK_ROLE, _msgSender()), "ERC777MintablePausableBlocklistable: must have block role to block");
        _blocklist[account] = true;
        emit Blocked(account);
    }
    
    /**
     * @dev Unblock account
     *
     * Requirements:
     *
     * - the caller must have the `BLOCK_ROLE`.
     */    
    function unblockAccount(address account) public virtual {
        require(hasRole(BLOCK_ROLE, _msgSender()), "ERC777MintablePausableBlocklistable: must have block role to unblock");
        _blocklist[account] = false;
        emit Unblocked(account);
    }

   /**
    * @return true if the user is blocked
    */
    function isBlockListed(address account) public view returns (bool) {
        return _blocklist[account];
    }

    /**
     * @dev See {ERC777-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the {MinterRole}.
     */
    function mint(
        address account,
        uint256 amount,
        bytes memory userData,
        bytes memory operatorData
    ) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC777MintablePausableBlocklistable: must have minter role to mint");
        require(!_mintingFinished, "ERC777MintablePausableBlocklistable: mint finished");
        
        _mint(account, amount, userData, operatorData);
    }

   /**
    * @return true if the minting is finished
    */
    function isFinishedMinting() public view returns (bool) {
        return _mintingFinished;
    }

    /**
    * @dev Function to stop minting new tokens.
    * @return True if the operation was successful.
    */
    function finishMinting() public returns (bool) {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC777MintablePausableBlocklistable: must have minter role to finish minting");
        _mintingFinished = true;

        emit MintFinished();
        return true;
    }

    /**
     * @dev See {ERC777-_beforeTokenTransfer}.
     *
     * Requirements:
     *
     * - the contract must not be paused.
     */
    function _beforeTokenTransfer(
        address operator, 
        address from, 
        address to, 
        uint256 amount
    )

        internal virtual override
    {
        super._beforeTokenTransfer(operator, from, to, amount);

        require(!paused(), "ERC20MintablePausableBlocklistable: token transfer while paused");
        require(!isBlockListed(operator), "ERC20MintablePausableBlocklistable: account is blocked");
        require(!isBlockListed(from), "ERC20MintablePausableBlocklistable: account is blocked");
        require(!isBlockListed(to), "ERC20MintablePausableBlocklistable: account is blocked");
    }

}
/**
 * @dev ERC777 token with pausable token transfers, minting and burning.
 *
 * Useful for scenarios such as preventing trades until the end of an evaluation
 * period, or having an emergency switch for freezing all token transfers in the
 * event of a large bug.
 *
 * _Available since v3.1._
 */
function _beforeTokenTransfer(
    address operator, 
    address from, 
    address to, 
    uint256 amount
)

    internal virtual override
{
    super._beforeTokenTransfer(operator, from, to, amount);

    require(!paused(), "ERC20MintablePausableBlocklistable: token transfer while paused");
    require(!isBlockListed(operator), "ERC20MintablePausableBlocklistable: account is blocked");
    require(!isBlockListed(from), "ERC20MintablePausableBlocklistable: account is blocked");
    require(!isBlockListed(to), "ERC20MintablePausableBlocklistable: account is blocked");
}

/**
 * @dev See {ERC777-_beforeTokenTransfer}.
 *
 * Requirements:
 *
 * - the contract must not be paused.
 */