transfer token fuel when return the investment

pragma solidity ^0.4.15;

import './AssetEvents.sol';
import '../token/Token.sol';

// Defines methods and control modifiers for an investment
contract AssetLibrary is AssetEvents {

    // Asset owner
    address public owner;
    // Asset currency
    string public currency;
    // Asset fixed value
    uint256 public fixedValue;
    // period to return the investment
    uint256 public paybackDays;
    // Gross return of investment
    uint256 public grossReturn;
    // Asset buyer
    address public investor;
    // Protocol version
    string public protocolVersion;
    // Contractual terms hash of investment
    bytes public assetTermsHash;
    // Document hash agreeing the contractual terms
    bytes public agreementHash;
    // investment timestamp
    uint public investedAt;
    // asset fuel
    Token public token;
    uint256 public tokenFuel;

    // possible stages of an asset
    enum Status {
        AVAILABLE,
        PENDING_OWNER_AGREEMENT,
        INVESTED,
        RETURNED,
        DELAYED_RETURN
    }
    Status public status;

    // Checks the current asset's status
    modifier hasStatus(Status _status) {
        assert(status == _status);
        _;
    }

    // Checks if the owner is the caller
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    // Checks if the investor is the caller
    modifier onlyInvestor() {
        require(msg.sender == investor);
        _;
    }

    // Compares the agreement terms hash of investor and owner
    modifier onlyAgreed(bytes _agreementHash) {
        require(keccak256(agreementHash) == keccak256(_agreementHash));
        _;
    }

    modifier onlyDelayed(){
        require(isDelayed());
        _;
    }

    function isDelayed()
        public
        returns(bool)
    {
        return now > investedAt + paybackDays * 1 days;
    }

    // Refund and remove the current investor and make the asset available for investments
    function makeAvailable()
        hasStatus(Status.PENDING_OWNER_AGREEMENT)
        private
        returns(address, uint256)
    {
        uint256 investedValue = this.balance;
        investor.transfer(investedValue);
        address currentInvestor = investor;
        investor = address(0);
        agreementHash = "";
        status = Status.AVAILABLE;
        investedAt = uint(0);
        return (currentInvestor, investedValue);
    }

    // Add investment interest in this asset and retain the funds within the smart contract
    function invest(address _investor, bytes _agreementHash) payable
         hasStatus(Status.AVAILABLE)
         public
         returns(bool)
    {
        investor = _investor;
        agreementHash = _agreementHash;
        investedAt = now;
        status = Status.PENDING_OWNER_AGREEMENT;
        Transferred(investor, owner, this.balance);
        return true;
    }

    // Cancel the pending investment
    function cancelInvestment()
        onlyInvestor
        hasStatus(Status.PENDING_OWNER_AGREEMENT)
        public
        returns(bool)
    {
        var (currentInvestor, investedValue) = makeAvailable();
        Canceled(owner, currentInvestor, investedValue);
        return true;
    }

    // Accept the investor as the asset buyer and withdraw funds
    function withdrawFunds(bytes _agreementHash)
        onlyOwner
        hasStatus(Status.PENDING_OWNER_AGREEMENT)
        onlyAgreed(_agreementHash)
        public
        returns(bool)
    {
        uint256 value = this.balance;
        owner.transfer(value);
        status = Status.INVESTED;
        Withdrawal(owner, investor, value, agreementHash);
        return true;
    }

    // Refuse the pending investment
    function refuseInvestment()
        onlyOwner
        hasStatus(Status.PENDING_OWNER_AGREEMENT)
        public
        returns(bool)
    {
        var (currentInvestor, investedValue) = makeAvailable();
        Refused(owner, currentInvestor, investedValue);
        return true;
    }

    function returnInvestment() payable
        onlyOwner
        hasStatus(Status.INVESTED)
        public
        returns(bool)
    {
        investor.transfer(msg.value);
        bool isDelayed = isDelayed();
        status = isDelayed ? Status.DELAYED_RETURN : Status.RETURNED;
        if(tokenFuel > 0){
            address recipient = isDelayed ? investor : owner;
            token.transferFrom(this, recipient, tokenFuel);
        }
        Returned(owner, investor, msg.value, isDelayed);
        return true;
    }

    function requireTokenFuel()
        onlyInvestor
        hasStatus(Status.INVESTED)
        onlyDelayed
    {
        token.transferFrom(this, investor, token.balanceOf(this));
    }

}

pragma solidity ^0.4.15;

import './AssetEvents.sol';
import '../token/Token.sol';

// Defines methods and control modifiers for an investment
contract AssetLibrary is AssetEvents {

    // Asset owner
    address public owner;
    // Asset currency
    string public currency;
    // Asset fixed value
    uint256 public fixedValue;
    // period to return the investment
    uint256 public paybackDays;
    // Gross return of investment
    uint256 public grossReturn;
    // Asset buyer
    address public investor;
    // Protocol version
    string public protocolVersion;
    // Contractual terms hash of investment
    bytes public assetTermsHash;
    // Document hash agreeing the contractual terms
    bytes public agreementHash;
    // investment timestamp
    uint public investedAt;
    // asset fuel
    Token public token;
    uint256 public tokenFuel;

    // possible stages of an asset
    enum Status {
        AVAILABLE,
        PENDING_OWNER_AGREEMENT,
        INVESTED,
        RETURNED,
        DELAYED_RETURN
    }
    Status public status;

    // Checks the current asset's status
    modifier hasStatus(Status _status) {
        assert(status == _status);
        _;
    }

    // Checks if the owner is the caller
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    // Checks if the investor is the caller
    modifier onlyInvestor() {
        require(msg.sender == investor);
        _;
    }

    // Compares the agreement terms hash of investor and owner
    modifier onlyAgreed(bytes _agreementHash) {
        require(keccak256(agreementHash) == keccak256(_agreementHash));
        _;
    }

    modifier onlyDelayed(){
        require(isDelayed());
        _;
    }

    function isDelayed()
        public
        returns(bool)
    {
        return now > investedAt + paybackDays * 1 days;
    }

    // Refund and remove the current investor and make the asset available for investments
    function makeAvailable()
        hasStatus(Status.PENDING_OWNER_AGREEMENT)
        private
        returns(address, uint256)
    {
        uint256 investedValue = this.balance;
        investor.transfer(investedValue);
        address currentInvestor = investor;
        investor = address(0);
        agreementHash = "";
        status = Status.AVAILABLE;
        investedAt = uint(0);
        return (currentInvestor, investedValue);
    }

    // Add investment interest in this asset and retain the funds within the smart contract
    function invest(address _investor, bytes _agreementHash) payable
         hasStatus(Status.AVAILABLE)
         public
         returns(bool)
    {
        investor = _investor;
        agreementHash = _agreementHash;
        investedAt = now;
        status = Status.PENDING_OWNER_AGREEMENT;
        Transferred(investor, owner, this.balance);
        return true;
    }

    // Cancel the pending investment
    function cancelInvestment()
        onlyInvestor
        hasStatus(Status.PENDING_OWNER_AGREEMENT)
        public
        returns(bool)
    {
        var (currentInvestor, investedValue) = makeAvailable();
        Canceled(owner, currentInvestor, investedValue);
        return true;
    }

    // Accept the investor as the asset buyer and withdraw funds
    function withdrawFunds(bytes _agreementHash)
        onlyOwner
        hasStatus(Status.PENDING_OWNER_AGREEMENT)
        onlyAgreed(_agreementHash)
        public
        returns(bool)
    {
        uint256 value = this.balance;
        owner.transfer(value);
        status = Status.INVESTED;
        Withdrawal(owner, investor, value, agreementHash);
        return true;
    }

    // Refuse the pending investment
    function refuseInvestment()
        onlyOwner
        hasStatus(Status.PENDING_OWNER_AGREEMENT)
        public
        returns(bool)
    {
        var (currentInvestor, investedValue) = makeAvailable();
        Refused(owner, currentInvestor, investedValue);
        return true;
    }

    function returnInvestment() payable
        onlyOwner
        hasStatus(Status.INVESTED)
        public
        returns(bool)
    {
        investor.transfer(msg.value);
        bool isDelayed = isDelayed();
        if (isDelayed) {
            status = Status.DELAYED_RETURN;
        } else {
            status = Status.RETURNED;
        }
        Returned(owner, investor, msg.value, isDelayed);
        return true;
    }

    function requireTokenFuel()
        onlyInvestor
        hasStatus(Status.INVESTED)
        onlyDelayed
    {
        token.transferFrom(this, investor, token.balanceOf(this));
    }

}


:rotating_light: Clear the warnings of chain manager

pragma solidity ^0.4.24;

import "../common/error.sol";

/// @title Chain Manager
/// @author ["Cryptape Technologies <contact@cryptape.com>"]
contract ChainManager is Error {

    address sysConfigAddr = 0xFFfffFFfFfFffFFfFFfffFffFfFFFffFFf020000;
    address crossChainVerifyAddr = 0xffFfffFfFFFfFFfffFFFffffFfFfffFfFF030002;

    // Id of the parent chain. 0 means no parent chain.
    uint32 parentChainId;

    // Nodes of the parent chain.
    address[] parentChainNodes;

    // Stores `ChainInfo` struct for each chain.
    mapping(uint32 => ChainInfo) public sideChains;

    // Default: Unknown
    enum ChainStatus { Unknown, Disable, Enable }

    struct ChainInfo {
        ChainStatus status;
        address[] nodes;
    }

    modifier hasParentChain {
        if (parentChainId != 0)
            _;
        else {
            emit ErrorLog(ErrorType.NoParentChain, "has no parent chain");
            return;
        }
    }

    modifier hasSideChain(uint32 _id) {
        if (sideChains[_id].status != ChainStatus.Unknown)
            _;
        else {
            emit ErrorLog(ErrorType.NoSideChain, "has no side chain");
            return;
        }
    }

    // Constructor.
    constructor(uint32 _pid, address[] _addrs)
        public
    {
        if (_pid == 0) {
            require(_addrs.length == 0);
        } else {
            require(_addrs.length > 0);
            parentChainId = _pid;
            parentChainNodes = _addrs;
        }
    }

    function getChainId()
        public
        returns (uint32)
    {
        address contractAddr = sysConfigAddr;
        bytes4 getChainIdHash = bytes4(keccak256("getChainId()"));

        uint256 result;
        uint256 cid;

        assembly {
            let ptr := mload(0x40)
            mstore(ptr, getChainIdHash)
            result := call(10000, contractAddr, 0, ptr, 0x4, ptr, 0x20)
            if eq(result, 0) { revert(ptr, 0) }
            cid := mload(ptr)
        }
        return uint32(cid);
    }

    // @notice Register a new side chain.
    function newSideChain(uint32 sideChainId, address[] addrs)
        public
    {
        require(addrs.length > 0);
        uint32 myChainId = getChainId();
        require(myChainId != sideChainId);
        require(sideChains[sideChainId].status == ChainStatus.Unknown);
        sideChains[sideChainId] = ChainInfo(ChainStatus.Disable, addrs);
        // TODO A sorted array can search data more fast.
        //      And we can remove duplicated data, simply.
    }

    function enableSideChain(uint32 id)
        public
        hasSideChain(id)
    {
        sideChains[id].status = ChainStatus.Enable;
    }

    function disableSideChain(uint32 id)
        public
        hasSideChain(id)
    {
        sideChains[id].status = ChainStatus.Disable;
    }

    function verifyBlockHeader(
        uint32 chainId,
        bytes blockHeader
    )
        public
        hasSideChain(chainId)
    {
        address contractAddr = crossChainVerifyAddr;
        bytes4 funcSig = bytes4(keccak256("verifyBlockHeader(uint32,bytes)"));
        bool verifyResult;
        uint blockHeaderSize = 0x20 + blockHeader.length / 0x20 * 0x20;
        if (blockHeader.length % 0x20 != 0) {
            blockHeaderSize += 0x20;
        }
        // bool
        uint outSize = 0x20;
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, funcSig)
            mstore(add(ptr, 0x04), chainId)
            mstore(add(ptr, 0x24), 0x40)
            let ptrL := add(ptr, 0x44)
            for {
                    let blockHeaderL := blockHeader
                    let blockHeaderR := add(blockHeader, blockHeaderSize)
                }
                lt(blockHeaderL, blockHeaderR)
                {
                    blockHeaderL := add(blockHeaderL, 0x20)
                    ptrL := add(ptrL, 0x20)
                }
                {
                mstore(ptrL, mload(blockHeaderL))
            }
            let inSize := sub(ptrL, ptr)
            let result := call(100000, contractAddr, 0, ptr, inSize, ptr, outSize)
            if eq(result, 0) { revert(ptr, 0) }
            verifyResult := mload(ptr)
        }
        require(verifyResult == true);
    }

    function getExpectedBlockNumber(
        uint32 chainId
    )
        public
        hasSideChain(chainId)
        returns (uint64)
    {
        address contractAddr = crossChainVerifyAddr;
        bytes4 funcSig = bytes4(keccak256("getExpectedBlockNumber(uint32)"));
        uint256 blockNumber;
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, funcSig)
            mstore(add(ptr, 0x04), chainId)
            let result := call(20000, contractAddr, 0, ptr, 0x24, ptr, 0x20)
            if eq(result, 0) { revert(ptr, 0) }
            blockNumber := mload(ptr)
        }
        return uint32(blockNumber);
    }

    function verifyState(
        uint32 chainId,
        uint64 blockNumber,
        bytes stateProof
    )
        public
        hasSideChain(chainId)
        returns (address, uint, uint)
    {
        address contractAddr = crossChainVerifyAddr;
        bytes4 funcSig = bytes4(keccak256("verifyState(uint32,uint64,bytes)"));
        uint stateProofSize = 0x20 + stateProof.length / 0x20 * 0x20;
        if (stateProof.length % 0x20 != 0) {
            stateProofSize += 0x20;
        }
        // address, key, value
        address addr;
        uint key;
        uint value;
        uint outSize = 0x60;
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, funcSig)
            mstore(add(ptr, 0x04), chainId)
            mstore(add(ptr, 0x24), blockNumber)
            mstore(add(ptr, 0x44), 0x60)
            let ptrL := add(ptr, 0x64)
            for {
                    let stateProofL := stateProof
                    let stateProofR := add(stateProof, stateProofSize)
                }
                lt(stateProofL, stateProofR)
                {
                    stateProofL := add(stateProofL, 0x20)
                    ptrL := add(ptrL, 0x20)
                }
                {
                mstore(ptrL, mload(stateProofL))
            }
            let inSize := sub(ptrL, ptr)
            let result := call(100000, contractAddr, 0, ptr, inSize, ptr, outSize)
            if eq(result, 0) { revert(ptr, 0) }
            addr := mload(ptr)
            key := mload(add(ptr, 0x20))
            value := mload(add(ptr, 0x40))
        }
        return (addr, key, value);
    }

    function getParentChainId()
        public
        view
        returns (uint32)
    {
        return parentChainId;
    }

    function getAuthorities(uint32 id)
        public
        view
        returns (address[])
    {
        // Is it the parent chain?
        if (parentChainId != 0 && parentChainId == id) {
            return parentChainNodes;
        // Is it a enabled side chain?
        } else if (sideChains[id].status == ChainStatus.Enable) {
            return sideChains[id].nodes;
        } else {
            // Returns an empty array;
        }
    }
}

pragma solidity ^0.4.24;

import "../common/error.sol";

/// @title Chain Manager
/// @author ["Cryptape Technologies <contact@cryptape.com>"]
contract ChainManager is Error {

    address sysConfigAddr = 0xFFfffFFfFfFffFFfFFfffFffFfFFFffFFf020000;
    address crossChainVerifyAddr = 0xffFfffFfFFFfFFfffFFFffffFfFfffFfFF030002;

    // Id of the parent chain. 0 means no parent chain.
    uint32 parentChainId;

    // Nodes of the parent chain.
    address[] parentChainNodes;

    // Stores `ChainInfo` struct for each chain.
    mapping(uint32 => ChainInfo) public sideChains;

    // Default: Unknown
    enum ChainStatus { Unknown, Disable, Enable }

    struct ChainInfo {
        ChainStatus status;
        address[] nodes;
    }

    modifier hasParentChain {
        if (parentChainId != 0)
            _;
        else {
            emit ErrorLog(ErrorType.NoParentChain, "has no parent chain");
            return;
        }
    }

    modifier hasSideChain(uint32 _id) {
        if (sideChains[_id].status != ChainStatus.Unknown)
            _;
        else {
            emit ErrorLog(ErrorType.NoSideChain, "has no side chain");
            return;
        }
    }

    // Constructor.
    constructor(uint32 _pid, address[] _addrs)
        public
    {
        if (_pid == 0) {
            require(_addrs.length == 0);
        } else {
            require(_addrs.length > 0);
            parentChainId = _pid;
            parentChainNodes = _addrs;
        }
    }

    function getChainId()
        public
        returns (uint32)
    {
        address contractAddr = sysConfigAddr;
        bytes4 getChainIdHash = bytes4(keccak256("getChainId()"));

        uint256 result;
        uint256 cid;

        assembly {
            let ptr := mload(0x40)
            mstore(ptr, getChainIdHash)
            result := call(10000, contractAddr, 0, ptr, 0x4, ptr, 0x20)
            if eq(result, 0) { revert(ptr, 0) }
            cid := mload(ptr)
        }
        return uint32(cid);
    }

    function getParentChainId()
        public
        hasParentChain
        returns (uint32)
    {
        return parentChainId;
    }

    // @notice Register a new side chain.
    function newSideChain(uint32 sideChainId, address[] addrs)
        public
    {
        require(addrs.length > 0);
        uint32 myChainId = getChainId();
        require(myChainId != sideChainId);
        require(sideChains[sideChainId].status == ChainStatus.Unknown);
        sideChains[sideChainId] = ChainInfo(ChainStatus.Disable, addrs);
        // TODO A sorted array can search data more fast.
        //      And we can remove duplicated data, simply.
    }

    function enableSideChain(uint32 id)
        public
        hasSideChain(id)
    {
        sideChains[id].status = ChainStatus.Enable;
    }

    function disableSideChain(uint32 id)
        public
        hasSideChain(id)
    {
        sideChains[id].status = ChainStatus.Disable;
    }

    function getAuthorities(uint32 id)
        public
        view
        returns (address[])
    {
        // Is it the parent chain?
        if (parentChainId != 0 && parentChainId == id) {
            return parentChainNodes;
        // Is it a enabled side chain?
        } else if (sideChains[id].status == ChainStatus.Enable) {
            return sideChains[id].nodes;
        } else {
            // Returns an empty array;
        }
    }

    function verifyBlockHeader(
        uint32 chainId,
        bytes blockHeader
    ) public hasSideChain(chainId) {
        address contractAddr = crossChainVerifyAddr;
        bytes4 funcSig = bytes4(keccak256("verifyBlockHeader(uint32,bytes)"));
        bool verifyResult;
        uint blockHeaderSize = 0x20 + blockHeader.length / 0x20 * 0x20;
        if (blockHeader.length % 0x20 != 0) {
            blockHeaderSize += 0x20;
        }
        // bool
        uint outSize = 0x20;
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, funcSig)
            mstore(add(ptr, 0x04), chainId)
            mstore(add(ptr, 0x24), 0x40)
            let ptrL := add(ptr, 0x44)
            for {
                    let blockHeaderL := blockHeader
                    let blockHeaderR := add(blockHeader, blockHeaderSize)
                }
                lt(blockHeaderL, blockHeaderR)
                {
                    blockHeaderL := add(blockHeaderL, 0x20)
                    ptrL := add(ptrL, 0x20)
                }
                {
                mstore(ptrL, mload(blockHeaderL))
            }
            let inSize := sub(ptrL, ptr)
            let result := call(100000, contractAddr, 0, ptr, inSize, ptr, outSize)
            if eq(result, 0) { revert(ptr, 0) }
            verifyResult := mload(ptr)
        }
        require(verifyResult == true);
    }

    function getExpectedBlockNumber(
        uint32 chainId
    ) public view hasSideChain(chainId) returns (uint64) {
        address contractAddr = crossChainVerifyAddr;
        bytes4 funcSig = bytes4(keccak256("getExpectedBlockNumber(uint32)"));
        uint256 blockNumber;
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, funcSig)
            mstore(add(ptr, 0x04), chainId)
            let result := call(20000, contractAddr, 0, ptr, 0x24, ptr, 0x20)
            if eq(result, 0) { revert(ptr, 0) }
            blockNumber := mload(ptr)
        }
        return uint32(blockNumber);
    }

    function verifyState(
        uint32 chainId,
        uint64 blockNumber,
        bytes stateProof
    ) public view hasSideChain(chainId) returns (address, uint, uint) {
        address contractAddr = crossChainVerifyAddr;
        bytes4 funcSig = bytes4(keccak256("verifyState(uint32,uint64,bytes)"));
        uint stateProofSize = 0x20 + stateProof.length / 0x20 * 0x20;
        if (stateProof.length % 0x20 != 0) {
            stateProofSize += 0x20;
        }
        // address, key, value
        address addr;
        uint key;
        uint value;
        uint outSize = 0x60;
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, funcSig)
            mstore(add(ptr, 0x04), chainId)
            mstore(add(ptr, 0x24), blockNumber)
            mstore(add(ptr, 0x44), 0x60)
            let ptrL := add(ptr, 0x64)
            for {
                    let stateProofL := stateProof
                    let stateProofR := add(stateProof, stateProofSize)
                }
                lt(stateProofL, stateProofR)
                {
                    stateProofL := add(stateProofL, 0x20)
                    ptrL := add(ptrL, 0x20)
                }
                {
                mstore(ptrL, mload(stateProofL))
            }
            let inSize := sub(ptrL, ptr)
            let result := call(100000, contractAddr, 0, ptr, inSize, ptr, outSize)
            if eq(result, 0) { revert(ptr, 0) }
            addr := mload(ptr)
            key := mload(add(ptr, 0x20))
            value := mload(add(ptr, 0x40))
        }
        return (addr, key, value);
    }
}