contract LBKBaseToken is DelayLockableToken {
    event LBKTransfer(address indexed from, address indexed to, uint256 value, string note);
    event LBKTransferFrom(address indexed owner, address indexed spender, address indexed to, uint256 value, string note);
    event LBKApproval(address indexed owner, address indexed spender, uint256 value, string note);

    event LBKMintTo(address indexed controller, address indexed to, uint256 amount, string note);
    event LBKBurnFrom(address indexed controller, address indexed from, uint256 value, string note);

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

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

    event LBKTransferToTeam(address indexed owner, address indexed spender, address indexed to, uint256 value, string note);
    event LBKTransferToPartner(address indexed owner, address indexed spender, address indexed to, uint256 value, string note);

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

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

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

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

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

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

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

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

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

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

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

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

    function lbkDecreaseApproval(address spender, uint256 subtractedValue, string note) public returns (bool ret) {
        ret = super.decreaseApproval(spender, subtractedValue);
        emit LBKApproval(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 lbkMintTo(address to, uint256 amount, string note) onlySuperOwner public returns (bool ret) {
        ret = mintTo(to, amount);
        emit LBKMintTo(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 lbkBurnFrom(address from, uint256 value, string note) onlyOwner public returns (bool ret) {
        ret = burnFrom(from, value);
        emit LBKBurnFrom(msg.sender, from, value, note);
    }

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

    function lbkBatchBurnWhenMoveToMainnet(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 && lbkBurnWhenMoveToMainnet(burners[i], values[i], note);
        }
    }

    /**
     * dev 이더로 LBK 를 구입하는 경우
     */
    function lbkSell(
        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 LEGALBLOCK. You cannot send money to this address.");

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

    /**
     * dev 비트코인 등의 다른 코인으로 LBK 를 구입하는 경우
     * dev EOA 가 트랜잭션을 일으켜서 처리해야 하기 때문에 다계좌를 기준으로 한다. (가스비 아끼기 위함)
     */
    function lbkBatchSellByOtherCoin(
        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 LEGALBLOCK. You cannot send money to this address.");

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

    /**
     * dev 팀에게 전송하는 경우
     */
    function lbkTransferToTeam(
        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 LEGALBLOCK. You cannot send money to this address.");

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

    /**
     * dev 파트너 및 어드바이저에게 전송하는 경우
     */
    function lbkTransferToPartner(
        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 LEGALBLOCK. You cannot send money to this address.");

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

    /**
     * dev 에코시스템(커뮤니티 활동을 통한 보상 등)으로 LBK 지급
     * dev EOA 가 트랜잭션을 일으켜서 처리해야 하기 때문에 다계좌를 기준으로 한다. (가스비 아끼기 위함)
     */
    function lbkBatchTransferToEcosystem(
        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 LEGALBLOCK. You cannot send money to this address.");

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

    /**
     * dev 바운티 참여자에게 LBK 지급
     * dev EOA 가 트랜잭션을 일으켜서 처리해야 하기 때문에 다계좌를 기준으로 한다. (가스비 아끼기 위함)
     */
    function lbkBatchTransferToBounty(
        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 LEGALBLOCK. You cannot send money to this address.");

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

    function destroy() onlyRoot public {
        selfdestruct(root);
    }
}
/**
 * @title LBKBaseToken
 * dev 트랜잭션 실행 시 메모를 남길 수 있도록 하였음.
 */
function lbkBatchSellByOtherCoin(
    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 LEGALBLOCK. You cannot send money to this address.");

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

/**
 * dev 비트코인 등의 다른 코인으로 LBK 를 구입하는 경우
 * dev EOA 가 트랜잭션을 일으켜서 처리해야 하기 때문에 다계좌를 기준으로 한다. (가스비 아끼기 위함)
 */