contract KSCBaseToken is DelayLockableToken {
    event KSCTransfer(address indexed from, address indexed to, uint256 value, string note);
    event KSCTransferFrom(address indexed owner, address indexed spender, address indexed to, uint256 value, string note);
    event KSCApproval(address indexed owner, address indexed spender, uint256 value, string note);

    event KSCMintTo(address indexed controller, address indexed to, uint256 amount, string note);
    event KSCBurnFrom(address indexed controller, address indexed from, uint256 value, string note);

    event KSCBurnWhenMoveToMainnet(address indexed controller, address indexed from, uint256 value, string note);

    event KSCSell(address indexed owner, address indexed spender, address indexed to, uint256 value, string note);
    event KSCSellByOtherCoin(address indexed owner, address indexed spender, address indexed to, uint256 value,  uint256 processIdHash, uint256 userIdHash, string note);

    event KSCTransferToTeam(address indexed owner, address indexed spender, address indexed to, uint256 value, string note);
    event KSCTransferToPartner(address indexed owner, address indexed spender, address indexed to, uint256 value, string note);

    event KSCTransferToEcosystem(address indexed owner, address indexed spender, address indexed to, uint256 value, uint256 processIdHash, uint256 userIdHash, string note);
    event KSCTransferToBounty(address indexed owner, address indexed spender, address indexed to, uint256 value, uint256 processIdHash, uint256 userIdHash, string note);

    // ERC20 함수들을 오버라이딩하여 super 로 올라가지 않고 무조건 ksc~ 함수로 지나가게 한다.
    function transfer(address to, uint256 value) public returns (bool ret) {
        return kscTransfer(to, value, "");
    }

    function kscTransfer(address to, uint256 value, string note) public returns (bool ret) {
        require(to != address(this), "The receive address is the Contact Address of KStarCoin. You cannot send money to this address.");

        ret = super.transfer(to, value);
        emit KSCTransfer(msg.sender, to, value, note);
    }

    function transferFrom(address from, address to, uint256 value) public returns (bool) {
        return kscTransferFrom(from, to, value, "");
    }

    function kscTransferFrom(address from, address to, uint256 value, string note) public returns (bool ret) {
        require(to != address(this), "The receive address is the Contact Address of KStarCoin. You cannot send money to this address.");

        ret = super.transferFrom(from, to, value);
        emit KSCTransferFrom(from, msg.sender, to, value, note);
    }

    function approve(address spender, uint256 value) public returns (bool) {
        return kscApprove(spender, value, "");
    }

    function kscApprove(address spender, uint256 value, string note) public returns (bool ret) {
        ret = super.approve(spender, value);
        emit KSCApproval(msg.sender, spender, value, note);
    }

    function increaseApproval(address spender, uint256 addedValue) public returns (bool) {
        return kscIncreaseApproval(spender, addedValue, "");
    }

    function kscIncreaseApproval(address spender, uint256 addedValue, string note) public returns (bool ret) {
        ret = super.increaseApproval(spender, addedValue);
        emit KSCApproval(msg.sender, spender, allowed[msg.sender][spender], note);
    }

    function decreaseApproval(address spender, uint256 subtractedValue) public returns (bool) {
        return kscDecreaseApproval(spender, subtractedValue, "");
    }

    function kscDecreaseApproval(address spender, uint256 subtractedValue, string note) public returns (bool ret) {
        ret = super.decreaseApproval(spender, subtractedValue);
        emit KSCApproval(msg.sender, spender, allowed[msg.sender][spender], note);
    }

    /**
     * dev 신규 화폐 발행. 반드시 이유를 메모로 남겨라.
     */
    function mintTo(address to, uint256 amount) internal returns (bool) {
        require(to != address(0x0), "This address to be set is zero address(0). Check the input address.");

        totalSupply_ = totalSupply_.add(amount);
        balances[to] = balances[to].add(amount);

        emit Transfer(address(0), to, amount);
        return true;
    }

    function kscMintTo(address to, uint256 amount, string note) onlyOwner public returns (bool ret) {
        ret = mintTo(to, amount);
        emit KSCMintTo(msg.sender, to, amount, note);
    }

    /**
     * dev 화폐 소각. 반드시 이유를 메모로 남겨라.
     */
    function burnFrom(address from, uint256 value) internal returns (bool) {
        require(value <= balances[from], "Your balance is insufficient.");

        balances[from] = balances[from].sub(value);
        totalSupply_ = totalSupply_.sub(value);

        emit Transfer(from, address(0), value);
        return true;
    }

    function kscBurnFrom(address from, uint256 value, string note) onlyOwner public returns (bool ret) {
        ret = burnFrom(from, value);
        emit KSCBurnFrom(msg.sender, from, value, note);
    }

    /**
     * dev 메인넷으로 이동하며 화폐 소각.
     */
    function kscBurnWhenMoveToMainnet(address burner, uint256 value, string note) onlyOwner public returns (bool ret) {
        ret = burnFrom(burner, value);
        emit KSCBurnWhenMoveToMainnet(msg.sender, burner, value, note);
    }

    function kscBatchBurnWhenMoveToMainnet(address[] burners, uint256[] values, string note) onlyOwner public returns (bool ret) {
        uint256 length = burners.length;
        require(length == values.length, "The size of \'burners\' and \'values\' array is different.");

        ret = true;
        for (uint256 i = 0; i < length; i++) {
            ret = ret && kscBurnWhenMoveToMainnet(burners[i], values[i], note);
        }
    }

    /**
     * dev 이더로 KSC 를 구입하는 경우
     */
    function kscSell(
        address from,
        address to,
        uint256 value,
        string note
    ) onlyOwner public returns (bool ret) {
        require(to != address(this), "The receive address is the Contact Address of KStarCoin. You cannot send money to this address.");

        ret = super.transferFrom(from, to, value);
        emit KSCSell(from, msg.sender, to, value, note);
    }

    /**
     * dev 비트코인 등의 다른 코인으로 KSC 를 구입하는 경우
     * dev EOA 가 트랜잭션을 일으켜서 처리해야 하기 때문에 다계좌를 기준으로 한다. (가스비 아끼기 위함)
     */
    function kscBatchSellByOtherCoin(
        address from,
        address[] to,
        uint256[] values,
        uint256 processIdHash,
        uint256[] userIdHash,
        string note
    ) onlyOwner public returns (bool ret) {
        uint256 length = to.length;
        require(length == values.length, "The size of \'to\' and \'values\' array is different.");
        require(length == userIdHash.length, "The size of \'to\' and \'userIdHash\' array is different.");

        ret = true;
        for (uint256 i = 0; i < length; i++) {
            require(to[i] != address(this), "The receive address is the Contact Address of KStarCoin. You cannot send money to this address.");

            ret = ret && super.transferFrom(from, to[i], values[i]);
            emit KSCSellByOtherCoin(from, msg.sender, to[i], values[i], processIdHash, userIdHash[i], note);
        }
    }

    /**
     * dev 팀에게 전송하는 경우
     */
    function kscTransferToTeam(
        address from,
        address to,
        uint256 value,
        string note
    ) onlyOwner public returns (bool ret) {
        require(to != address(this), "The receive address is the Contact Address of KStarCoin. You cannot send money to this address.");

        ret = super.transferFrom(from, to, value);
        emit KSCTransferToTeam(from, msg.sender, to, value, note);
    }

    /**
     * dev 파트너 및 어드바이저에게 전송하는 경우
     */
    function kscTransferToPartner(
        address from,
        address to,
        uint256 value,
        string note
    ) onlyOwner public returns (bool ret) {
        require(to != address(this), "The receive address is the Contact Address of KStarCoin. You cannot send money to this address.");

        ret = super.transferFrom(from, to, value);
        emit KSCTransferToPartner(from, msg.sender, to, value, note);
    }

    /**
     * dev 에코시스템(커뮤니티 활동을 통한 보상 등)으로 KSC 지급
     * dev EOA 가 트랜잭션을 일으켜서 처리해야 하기 때문에 다계좌를 기준으로 한다. (가스비 아끼기 위함)
     */
    function kscBatchTransferToEcosystem(
        address from, address[] to,
        uint256[] values,
        uint256 processIdHash,
        uint256[] userIdHash,
        string note
    ) onlyOwner public returns (bool ret) {
        uint256 length = to.length;
        require(length == values.length, "The size of \'to\' and \'values\' array is different.");
        require(length == userIdHash.length, "The size of \'to\' and \'userIdHash\' array is different.");

        ret = true;
        for (uint256 i = 0; i < length; i++) {
            require(to[i] != address(this), "The receive address is the Contact Address of KStarCoin. You cannot send money to this address.");

            ret = ret && super.transferFrom(from, to[i], values[i]);
            emit KSCTransferToEcosystem(from, msg.sender, to[i], values[i], processIdHash, userIdHash[i], note);
        }
    }

    /**
     * dev 바운티 참여자에게 KSC 지급
     * dev EOA 가 트랜잭션을 일으켜서 처리해야 하기 때문에 다계좌를 기준으로 한다. (가스비 아끼기 위함)
     */
    function kscBatchTransferToBounty(
        address from,
        address[] to,
        uint256[] values,
        uint256 processIdHash,
        uint256[] userIdHash,
        string note
    ) onlyOwner public returns (bool ret) {
        uint256 length = to.length;
        require(to.length == values.length, "The size of \'to\' and \'values\' array is different.");

        ret = true;
        for (uint256 i = 0; i < length; i++) {
            require(to[i] != address(this), "The receive address is the Contact Address of KStarCoin. You cannot send money to this address.");

            ret = ret && super.transferFrom(from, to[i], values[i]);
            emit KSCTransferToBounty(from, msg.sender, to[i], values[i], processIdHash, userIdHash[i], note);
        }
    }

    function destroy() onlyRoot public {
        selfdestruct(root);
    }
}
/**
 * @title KSCBaseToken
 * dev 트랜잭션 실행 시 메모를 남길 수 있도록 하였음.
 */
function mintTo(address to, uint256 amount) internal returns (bool) {
        require(to != address(0x0), "This address to be set is zero address(0). Check the input address.");

        totalSupply_ = totalSupply_.add(amount);
        balances[to] = balances[to].add(amount);

        emit Transfer(address(0), to, amount);
        return true;
    }
/**
 * dev 신규 화폐 발행. 반드시 이유를 메모로 남겨라.
 */