// Contract: TokenFactory
// Target function: function increaseAllowance(address _spender, uint _addedValue)
 used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a `Transfer` event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _balances[sender] = _balances[sender].sub(amount);
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a `Transfer` event with `from` set to the zero address.
     *
     * Requirements
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to the zero address");

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

     /**
     * @dev Destoys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a `Transfer` event with `to` set to the zero address.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 value) internal {
        require(account != address(0), "ERC20: burn from the zero address");

        _totalSupply = _totalSupply.sub(value);
        _balances[account] = _balances[account].sub(value);
        emit Transfer(account, address(0), value);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
     *
     * This is internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an `Approval` event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    /**
     * @dev Destoys `amount` tokens from `account`.`amount` is then deducted
     * from the caller's allowance.
     *
     * See `_burn` and `_approve`.
     */
    function _burnFrom(address account, uint256 amount) internal {
        _burn(account, amount);
        _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount));
    }
}

/**
 * @dev Extension of `ERC20` allows a centralized owner to burn users' tokens
 *
 * At construction time, the deployer of the contract is the only burner.
 */
contract ERC20Operator is ERC20, OperatorRole {

    event ForcedTransfer(address requester, address from, address to, uint256 value);

    /**
     * @dev new function to burn tokens from a centralized owner
     * @param _from address The address which the operator wants to send tokens from
     * @param _to address The address which the operator wants to transfer to
     * @param _value uint256 the amount of tokens to be transferred
     * @return A boolean that indicates if the operation was successful.
     */
    function forcedTransfer(address _from, address _to, uint256 _value)
        public
        onlyOperator
        returns (bool) {
            _transfer(_from, _to, _value);
            emit ForcedTransfer(msg.sender, _from, _to, _value);
            return true;
    }
}

/**
 * @dev Extension of `ERC20` that adds a set of accounts with the `MinterRole`,
 * which have permission to mint (create) new tokens as they see fit.
 *
 * At construction, the deployer of the contract is the only minter.
 */
contract ERC20Mintable is ERC20, MinterRole {
    /**
     * @dev See `ERC20._mint`.
     *
     * Requirements:
     *
     * - the caller must have the `MinterRole`.
     */
    function mint(address account, uint256 amount) public onlyMinter returns (bool) {
        _mint(account, amount);
        return true;
    }
}

/**
 * @dev Modification of OpenZeppelin's ERC20Capped. Implements a mechanism
 * to enable and disable cap control, as well as cap modification.
 */
contract ERC20CapEnabler is ERC20Mintable, CreatorRole {

    uint256 public cap;
    bool public capEnabled;

    event CapEnabled(address sender);
    event CapDisabled(address sender);
    event CapSet(address sender, uint256 amount);

    /**
     * @dev Enable cap control on minting.
     */
    function enableCap()
        external
        onlyCreator {
            capEnabled = true;
            emit CapEnabled(msg.sender);
    }

    /**
     * @dev Disable cap control on minting and set cap back to 0.
     */
    function disableCap()
        external
        onlyCreator {
            capEnabled = false;
            // set cap to 0
            cap = 0;
            emit CapDisabled(msg.sender);
    }

    /**
     * @dev Set a new cap.
     */
    function setCap(uint256 _newCap)
        external
        onlyCreator {
            cap = _newCap;
            emit CapSet(msg.sender, _newCap);
    }

    /**
     * @dev Overrides mint by checking whether cap control is enabled and
     * reverting if the token addition to supply will exceed the cap.
     */
    function mint(address account, uint256 value)
        public
        onlyMinter
        returns (bool) {
            if (capEnabled) require(totalSupply().add(value) <= cap, "ERC20CapEnabler: cap exceeded");
            return super.mint(account, value);
    }

}

/**
 * @title Pausable token
 * @dev ERC20 modified with pausable transfers.
 */
contract ERC20Pausable is ERC20, Pausable {
    function transfer(address to, uint256 value) public whenNotPaused returns (bool) {
        return super.transfer(to, value);
    }

    function transferFrom(address from, address to, uint256 value) public whenNotPaused returns (bool) {
        return super.transferFrom(from, to, value);
    }

    function approve(address spender, uint256 value) public whenNotPaused returns (bool) {
        return super.approve(spender, value);
    }

    function increaseAllowance(address spender, uint addedValue) public whenNotPaused returns (bool) {
        return super.increaseAllowance(spender, addedValue);
    }

    function decreaseAllowance(address spender, uint subtractedValue) public whenNotPaused returns (bool) {
        return super.decreaseAllowance(spender, subtractedValue);
    }
}

/**
 * @dev Extension of `ERC20` allows a centralized owner to burn users' tokens
 *
 * At construction time, the deployer of the contract is the only burner.
 */
contract ERC20BurnableAdmin is ERC20, BurnerRole {

    event ForcedBurn(address requester, address wallet, uint256 value);

    /**
     * @dev new function to burn tokens from a centralized owner
     * @param _who The address which will be burned.
     * @param _value The amount of tokens to burn.
     * @return A boolean that indicates if the operation was successful.
     */
    function forcedBurn(address _who, uint256 _value)
        public
        onlyBurner
        returns (bool) {
            _burn(_who, _value);
            emit ForcedBurn(msg.sender, _who, _value);
            return true;
    }
}

/**
 * @dev Optional functions from the ERC20 standard.
 */
contract ERC20Detailed is IERC20 {
    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
     * these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name, string memory symbol, uint8 decimals) public {
        _name = name;
        _symbol = symbol;
        _decimals = decimals;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei.
     *
     * > Note that this information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * `IERC20.balanceOf` and `IERC20.transfer`.
     */
    function decimals() public view returns (uint8) {
        return _decimals;
    }
}

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be aplied to your functions to restrict their use to
 * the owner.
 */
contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), _owner);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _owner;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * > Note: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

contract TokenAccessList is Ownable {

    string public identifier;
    mapping(address => bool) private accessList;

    event WalletEnabled(address indexed wallet);
    event WalletDisabled(address indexed wallet);

    constructor(string memory _identifier) public {
        identifier = _identifier;
    }

    function enableWallet(address _wallet)
        public
        onlyOwner
        {
            require(_wallet != address(0), "Invalid wallet");
            accessList[_wallet] = true;
            emit WalletEnabled(_wallet);
    }

    function disableWallet(address _wallet)
        public
        onlyOwner
        {
            accessList[_wallet] = false;
            emit WalletDisabled(_wallet);
    }

    function enableWalletList(address[] calldata _walletList)
        external
        onlyOwner {
            for(uint i = 0; i < _walletList.length; i++) {
                enableWallet(_walletList[i]);
            }
    }

    function disableWalletList(address[] calldata _walletList)
        external
        onlyOwner {
            for(uint i = 0; i < _walletList.length; i++) {
                disableWallet(_walletList[i]);
            }
    }

    function checkEnabled(address _wallet)
        public
        view
        returns (bool) {
            return _wallet == address(0) || accessList[_wallet];
    }

    function checkEnabledList(address _w1, address _w2, address _w3)
        external
        view
        returns (bool) {
            return checkEnabled(_w1)
                && checkEnabled(_w2)
                && checkEnabled(_w3);
    }

}

/**
 * ERC20 implementation that optionally allows the setup of a access list,
 * which may or may not be required by regulators. If a access list is
 * configured, then the contract starts validating parties.
 * Only the token creator, represented by the CreatorRole, is allowed
 * to add and remove the access list
 */
contract ERC20AccessList is ERC20Pausable, ERC20CapEnabler, ERC20Operator {

    TokenAccessList public accessList;
    bool public checkingAccessList;
    address constant private EMPTY_ADDRESS = address(0);

    /**
     * Admin events
     */

    event AccessListSet(address accessList);
    event AccessListUnset();

    modifier hasAccess(address _w1, address _w2, address _w3) {
        if (checkingAccessList) {
            require(accessList.checkEnabledList(_w1, _w2, _w3), "AccessList: address not authorized");
        }
        _;
    }

    /**
    * Admin functions
    */

    /**
    * @dev Sets up the centralized accessList contract
    * @param _accessList the address of accessList contract.
    * @return A boolean that indicates if the operation was successful.
    */
    function setupAccessList(address _accessList)
        public
        onlyCreator {
            require(_accessList != address(0), "Invalid access list address");
            accessList = TokenAccessList(_accessList);
            checkingAccessList = true;
            emit AccessListSet(_accessList);
    }

    /**
    * @dev Removes the accessList
    * @return A boolean that indicates if the operation was successful.
    */
    function removeAccessList()
        public
        onlyCreator {
            checkingAccessList = false;
            accessList = TokenAccessList(0x0);
            emit AccessListUnset();
    }

    /**
    * @dev Overrides MintableToken mint() adding the accessList validation
    * @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
        onlyMinter
        hasAccess(_to, EMPTY_ADDRESS, EMPTY_ADDRESS)
        returns (bool) {
            return super.mint(_to, _amount);
    }

    /**
    * User functions
    */

    /**
    * @dev Overrides BasicToken transfer() adding the accessList validation
    * @param _to The address to transfer to.
    * @param _value The amount to be transferred.
    * @return A boolean that indicates if the operation was successful.
    */
    function transfer(address _to, uint256 _value)
        public
        whenNotPaused
        hasAccess(msg.sender, _to, EMPTY_ADDRESS)
        returns (bool) {
            return super.transfer(_to, _value);
    }

    /**
    * @dev Overrides BasicToken transfer() adding the accessList validation
    * @param _to The address to transfer from.
    * @param _to The address to transfer to.
    * @param _value The amount to be transferred.
    * @return A boolean that indicates if the operation was successful.
    */
    function forcedTransfer(address _from, address _to, uint256 _value)
        public
        whenNotPaused
        hasAccess(_from, _to, EMPTY_ADDRESS)
        returns (bool) {
            return super.forcedTransfer(_from, _to, _value);
    }

    /**
     * @dev Overrides StandardToken transferFrom() adding the accessList validation
     * @param _from address The address which you want to send tokens from
     * @param _to address The address which you want to transfer to
     * @param _value uint256 the amount of tokens to be transferred
     */
    function transferFrom(address _from, address _to, uint256 _value)
        public
        whenNotPaused
        hasAccess(msg.sender, _from, _to)
        returns (bool) {
            return super.transferFrom(_from, _to, _value);
    }

    /**
     * @dev Overrides StandardToken approve() adding the accessList validation
     * @param _spender The address which will spend the funds.
     * @param _value The amount of tokens to be spent.
     * @return A boolean that indicates if the operation was successful.
     */
    function approve(address _spender, uint256 _value)
        public
        whenNotPaused
        hasAccess(msg.sender, _spender, EMPTY_ADDRESS)
        returns (bool) {
            return super.approve(_spender, _value);
    }