contract CCIToken is LockableToken {

    // name of the token
    string private _name = "Cryptocurrency Index";
    // symbol of the token
    string private _symbol = "CCI";
    // decimals of the token
    uint8 private _decimals = 8;

    // initial supply
    uint256 internal constant INITIAL_SUPPLY = 1000000 * (10 ** 8);

    // registered contracts (to prevent loss of token via transfer function)
    mapping (address => bool) private _contracts;

    /**
     * @dev constructor function that is called once at deployment of the contract.
     * @param recipient Address to receive initial supply.
     */
    constructor(address recipient, address initialOwner) public Ownable(initialOwner) {

        _mint(recipient, INITIAL_SUPPLY);

    }

    /**
     * @dev Allows to send tokens (via Approve and TransferFrom) to other smart contract.
     * @param spender Address of smart contracts to work with.
     * @param amount Amount of tokens to send.
     * @param extraData Any extra data.
     */
    function approveAndCall(address spender, uint256 amount, bytes memory extraData) public returns (bool) {
        require(approve(spender, amount));

        ApproveAndCallFallBack(spender).receiveApproval(msg.sender, amount, address(this), extraData);

        return true;
    }

    /**
     * @dev Allows to register other smart contracts (to prevent loss of tokens via transfer function).
     * @param addr Address of smart contracts to work with.
     */
    function registerContract(address addr) public onlyOwner {
        require(isContract(addr));
        _contracts[addr] = true;
    }

    /**
     * @dev Allows to unregister registered smart contracts.
     * @param addr Address of smart contracts to work with.
     */
    function unregisterContract(address addr) external onlyOwner {
        _contracts[addr] = false;
    }

    /**
     * @dev modified transfer function that allows to safely send tokens to smart contract.
     * @param to The address to transfer to.
     * @param value The amount to be transferred.
     */
    function transfer(address to, uint256 value) public returns (bool) {

        if (_contracts[to]) {
            approveAndCall(to, value, new bytes(0));
        } else {
            super.transfer(to, value);
        }

        return true;

    }

    /**
     * @dev modified transferFrom function that allows to safely send tokens to exchange contract.
     * @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 returns (bool) {

        if (_contracts[to] && !_contracts[msg.sender]) {
            ApproveAndCallFallBack(to).receiveApproval(msg.sender, value, address(this), new bytes(0));
        } else {
            super.transferFrom(from, to, value);
        }

        return true;
    }

    /**
     * @dev Allows to any owner of the contract withdraw needed ERC20 token from this contract (promo or bounties for example).
     * @param ERC20Token Address of ERC20 token.
     * @param recipient Account to receive tokens.
     */
    function withdrawERC20(address ERC20Token, address recipient) external onlyOwner {

        uint256 amount = IERC20(ERC20Token).balanceOf(address(this));
        IERC20(ERC20Token).transfer(recipient, amount);

    }

    /**
     * @return the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @return the symbol of the token.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /**
     * @return the number of decimals of the token.
     */
    function decimals() public view returns (uint8) {
        return _decimals;
    }

    /**
     * @return true if the address is a сontract
     */
    function isContract(address addr) internal view returns (bool) {
        uint size;
        assembly { size := extcodesize(addr) }
        return size > 0;
    }

}
/**
 * @title The CCIToken project contract.
 * @author https://grox.solutions
 */
function symbol() public view returns (string memory) {
    return _symbol;
}

/**
 * @return the symbol of the token.
 */