Accounts
0xc0...322a
X-Saving Certificate

X-Saving Certificate

$0.00
This contract's source code is verified!
Contract Metadata
Compiler
0.4.26+commit.4563c3fc
Language
Solidity
Contract Source Code
File 1 of 1: CoolBitETFUSDTAndCompound.sol
pragma solidity ^0.4.24;

interface ITetherERC20 {
    function totalSupply() public view returns (uint supply);
    function balanceOf(address _owner) public view returns (uint balance);
    function transfer(address _to, uint _value) public;
    function transferFrom(address _from, address _to, uint _value) public;
    function approve(address _spender, uint _value) public;
    function allowance(address _owner, address _spender) public view returns (uint remaining);
    function decimals() public view returns(uint8 digits);
    event Approval(address indexed _owner, address indexed _spender, uint _value);
}

/**
 * @title ERC20Basic
 * @dev Simpler version of ERC20 interface
 * See https://github.com/ethereum/EIPs/issues/179
 */
contract ERC20Basic {
  function totalSupply() public view returns (uint256);
  function balanceOf(address who) public view returns (uint256);
  function transfer(address to, uint256 value) public returns (bool);
  event Transfer(address indexed from, address indexed to, uint256 value);
}

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
contract ERC20 is ERC20Basic {
  function allowance(address owner, address spender) public view returns (uint256);
  function transferFrom(address from, address to, uint256 value) public returns (bool);
  function approve(address spender, uint256 value) public returns (bool);
  event Approval(address indexed owner, address indexed spender, uint256 value);
}

/**
 * @title Basic token
 * @dev Basic version of StandardToken, with no allowances.
 */
contract BasicToken is ERC20Basic {
  using SafeMath for uint256;

  mapping(address => uint256) balances; // Storage slot 0

  uint256 totalSupply_; // Storage slot 1

  /**
  * @dev Total number of tokens in existence
  */
  function totalSupply() public view returns (uint256) {
    return totalSupply_;
  }

  /**
  * @dev Transfer token for a specified address
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[msg.sender]);

    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    emit Transfer(msg.sender, _to, _value);
    return true;
  }

  /**
  * @dev Gets the balance of the specified address.
  * @param _owner The address to query the the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address _owner) public view returns (uint256) {
    return balances[_owner];
  }

}

/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* https://github.com/ethereum/EIPs/issues/20
* Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
    using SafeMath for uint256;

    mapping (address => mapping (address => uint256)) internal allowed; // Storage slot 2

    /**
    * @dev Transfer tokens from one address to another
    * @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)
    {
        require(_to != address(0));
        require(_value <= balances[_from]);
        require(_value <= allowed[_from][msg.sender]);

        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        emit Transfer(_from, _to, _value);
        return true;
    }

    /**
    * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
    * Beware that changing an allowance with this method brings the risk that someone may use both the old
    * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
    * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
    * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    * @param _spender The address which will spend the funds.
    * @param _value The amount of tokens to be spent.
    */
    function approve(address _spender, uint256 _value) public returns (bool) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    /**
    * @dev Function to check the amount of tokens that an owner allowed to a spender.
    * @param _owner address The address which owns the funds.
    * @param _spender address The address which will spend the funds.
    * @return A uint256 specifying the amount of tokens still available for the spender.
    */
    function allowance(
        address _owner,
        address _spender
    )
        public
        view
        returns (uint256)
    {
        return allowed[_owner][_spender];
    }

    /**
    * @dev Increase the amount of tokens that an owner allowed to a spender.
    * approve should be called when allowed[_spender] == 0. To increment
    * allowed value is better to use this function to avoid 2 calls (and wait until
    * the first transaction is mined)
    * From MonolithDAO Token.sol
    * @param _spender The address which will spend the funds.
    * @param _addedValue The amount of tokens to increase the allowance by.
    */
    function increaseApproval(
        address _spender,
        uint256 _addedValue
    )
        public
        returns (bool)
    {
        allowed[msg.sender][_spender] = (
        allowed[msg.sender][_spender].add(_addedValue));
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    /**
    * @dev Decrease the amount of tokens that an owner allowed to a spender.
    * approve should be called when allowed[_spender] == 0. To decrement
    * allowed value is better to use this function to avoid 2 calls (and wait until
    * the first transaction is mined)
    * From MonolithDAO Token.sol
    * @param _spender The address which will spend the funds.
    * @param _subtractedValue The amount of tokens to decrease the allowance by.
    */
    function decreaseApproval(
        address _spender,
        uint256 _subtractedValue
    )
        public
        returns (bool)
    {
        uint256 oldValue = allowed[msg.sender][_spender];
        if (_subtractedValue > oldValue) {
        allowed[msg.sender][_spender] = 0;
        } else {
        allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
        }
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

}

contract StandardTokenMintableBurnable is StandardToken {
  using SafeMath for uint256;

  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);
  }

  function burn(uint256 amount) public {
    _burn(msg.sender, amount);
  }

  function _burn(address account, uint256 amount) internal {
    require(account != address(0), "ERC20: burn from the zero address");
    totalSupply_ = totalSupply_.sub(amount);
    balances[account] = balances[account].sub(amount);
    emit Transfer(account, address(0), amount);
  }
}

contract WhiteListToken is StandardTokenMintableBurnable{
  address public whiteListAdmin;
  bool public isTransferRestricted;
  bool public isReceiveRestricted;
  mapping(address => bool) public transferWhiteList;
  mapping(address => bool) public receiveWhiteList;


  constructor(address _admin) public {
    whiteListAdmin = _admin;
    isReceiveRestricted = true;
  }

  modifier isWhiteListAdmin() {
      require(msg.sender == whiteListAdmin);
      _;
  }

  function transfer(address _to, uint256 _value) public returns (bool){
    if (isTransferRestricted) {
      require(transferWhiteList[msg.sender], "only whitelist senders can transfer tokens");
    }
    if (isReceiveRestricted) {
      require(receiveWhiteList[_to], "only whiteList receivers can receive tokens");
    }
    return super.transfer(_to, _value);
  }


  function transferFrom(address _from, address _to, uint256 _value) public returns (bool){
    if (isTransferRestricted) {
      require(transferWhiteList[_from], "only whiteList senders can transfer tokens");
    }
    if (isReceiveRestricted) {
      require(receiveWhiteList[_to], "only whiteList receivers can receive tokens");
    }
    return super.transferFrom(_from, _to, _value);
  }

  function enableTransfer() isWhiteListAdmin public {
    require(isTransferRestricted);
    isTransferRestricted = false;
  }

  function restrictTransfer() isWhiteListAdmin public {
    require(isTransferRestricted == false);
    isTransferRestricted = true;
  }

  function enableReceive() isWhiteListAdmin public {
    require(isReceiveRestricted);
    isReceiveRestricted = false;
  }

  function restrictReceive() isWhiteListAdmin public {
    require(isReceiveRestricted == false);
    isReceiveRestricted = true;
  }


  function removeTransferWhiteListAddress(address _whiteListAddress) public isWhiteListAdmin returns(bool) {
    require(transferWhiteList[_whiteListAddress]);
    transferWhiteList[_whiteListAddress] = false;
    return true;
  }

  function addTransferWhiteListAddress(address _whiteListAddress) public isWhiteListAdmin returns(bool) {
    require(transferWhiteList[_whiteListAddress] == false);
    transferWhiteList[_whiteListAddress] = true;
    return true;
  }

  function removeReceiveWhiteListAddress(address _whiteListAddress) public isWhiteListAdmin returns(bool) {
    require(receiveWhiteList[_whiteListAddress]);
    receiveWhiteList[_whiteListAddress] = false;
    return true;
  }

  function addReceiveWhiteListAddress(address _whiteListAddress) public isWhiteListAdmin returns(bool) {
    require(receiveWhiteList[_whiteListAddress] == false);
    receiveWhiteList[_whiteListAddress] = true;
    return true;
  }

}

/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
    if (a == 0) {
      return 0;
    }

    c = a * b;
    assert(c / a == b);
    return c;
  }

  /**
  * @dev Integer division of two numbers, truncating the quotient.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    // uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return a / b;
  }

  /**
  * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  /**
  * @dev Adds two numbers, throws on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
    c = a + b;
    assert(c >= a);
    return c;
  }
}

contract SimpleOracleAccruedRatioUSD {
    using SafeMath for uint256;
    address public admin;
    address public superAdmin;
    uint256 public accruedRatioUSD;
    uint256 public lastUpdateTime;
    uint256 public MAXIMUM_CHANGE_PCT = 3;

    constructor(uint256 _accruedRatioUSD, address _admin, address _superAdmin) public {
        admin = _admin;
        superAdmin = _superAdmin;
        accruedRatioUSD = _accruedRatioUSD;
    }

    modifier onlyAdmin {
        require(msg.sender == admin || msg.sender == superAdmin);
        _;
    }

    modifier onlySuperAdmin {
        require(msg.sender == superAdmin);
        _;
    }

    function isValidRatio(uint256 _accruedRatioUSD) view internal {
      require(_accruedRatioUSD >= accruedRatioUSD, "ratio should be monotonically increased");
      uint256 maximumChange = accruedRatioUSD.mul(MAXIMUM_CHANGE_PCT).div(100);
      require(_accruedRatioUSD.sub(accruedRatioUSD) < maximumChange, "exceeds maximum chagne");
    }

    function checkTimeStamp() view internal {
      // 82800 = 23 * 60 * 60  (23 hours)
      require(block.timestamp.sub(lastUpdateTime) > 82800, "oracle are not allowed to update two times within 23 hours");
    }

    function set(uint256 _accruedRatioUSD) onlyAdmin public{
        if(msg.sender != superAdmin) {
          isValidRatio(_accruedRatioUSD);
          checkTimeStamp();
        }
        lastUpdateTime = block.timestamp;
        accruedRatioUSD = _accruedRatioUSD;
    }

    function query() external view returns(uint256)  {
        // QueryEvent(msg.sender, block.number);
        return accruedRatioUSD;
    }
}

interface CERC20 {
    function mint(uint mintAmount) returns (uint);
    function redeem(uint redeemTokens) returns (uint);
    function supplyRatePerBlock() returns (uint);
    function exchangeRateCurrent() returns (uint);
    function balanceOf(address _owner) public view returns (uint balance);
    function balanceOfUnderlying(address account) returns (uint);
}

interface CEther {
    function mint() payable;
    function redeem(uint redeemTokens) returns (uint);
    function supplyRatePerBlock() returns (uint);
    function balanceOf(address _owner) public view returns (uint balance);
    function balanceOfUnderlying(address account) returns (uint);
}

contract CoolBitETFUSDTAndCompound is WhiteListToken{
    using SafeMath for uint256;

    uint256 public baseRatio;
    string public name = "X-Saving Certificate";
    string public constant symbol = "XSCert";
    uint8 public decimals;

    // USDT token contract
    ITetherERC20 public StableToken;
    SimpleOracleAccruedRatioUSD public oracle;
    // Defi contract
    CERC20 public cToken;

    // Roles
    address public bincentiveHot; // i.e., Platform Owner
    address public bincentiveCold;
    address[] public investors;
    mapping(address => bool) public isInInvestorList;

    uint256 public numAUMDistributedInvestors; // i.e., number of investors that already received AUM

    // Contract(Fund) Status
    // 0: not initialized
    // 1: initialized
    // 2: not enough fund came in in time
    // 3: fundStarted
    // 4: running
    // 5: stoppped
    // 6: closed
    // 7: suspended
    uint256 public fundStatus;

    // Money
    mapping(address => uint256) public investorDepositUSDTAmount;  // denominated in stable token
    uint256 public currentInvestedAmount;  // denominated in stable token

    // Fund Parameters
    uint256 public investPaymentDueTime;  // deadline for deposit which comes in before fund starts running
    uint256 public percentageOffchainFund;  // percentage of fund that will be transfered off-chain
    uint256 public percentageMinimumFund;  // minimum percentage of fund required to keep the fund functioning
    uint256 public minimumFund;  // minimum amounf required to keep the fund functioning
    uint256 public minPenalty;  // a minimum 100 USDT penalty

    // Events
    event Deposit(address indexed investor, uint256 investAmount, uint256 mintedAmount);
    event UserInfo(bytes32 indexed uuid, string referralCode);
    event StartFund(uint256 timeStamp, uint256 num_investors, uint256 totalInvestedAmount, uint256 totalMintedTokenAmount);
    event Withdraw(address indexed investor, uint256 tokenAmount, uint256 USDTAmount, uint256 ToBincentiveColdUSDTAmount);
    event MidwayQuit(address indexed investor, uint256 tokenAmount, uint256 USDTAmount);
    event ReturnAUM(uint256 StableTokenAmount);
    event DistributeAUM(address indexed to, uint256 tokenAmount, uint256 StableTokenAmount);
    // Admin Events
    event NewBincentiveCold(address newBincentiveCold);
    // Defi Events
    event MintcUSDT(uint USDTAmount);
    event RedeemcUSDT(uint RedeemcUSDTAmount);

    // Modifiers
    modifier initialized() {
        require(fundStatus == 1);
        _;
    }

    // modifier fundStarted() {
    //     require(fundStatus == 3);
    //     _;
    // }

    modifier running() {
        require(fundStatus == 4);
        _;
    }

    modifier runningOrSuspended() {
        require((fundStatus == 4) || (fundStatus == 7));
        _;
    }

    modifier stoppedOrSuspended() {
        require((fundStatus == 5) || (fundStatus == 7));
        _;
    }

    modifier runningOrStoppedOrSuspended() {
        require((fundStatus == 4) || (fundStatus == 5) || (fundStatus == 7));
        _;
    }

    modifier closedOrAbortedOrSuspended() {
        require((fundStatus == 6) || (fundStatus == 2) || (fundStatus == 7));
        _;
    }

    modifier isBincentive() {
        require(
            (msg.sender == bincentiveHot) || (msg.sender == bincentiveCold)
        );
        _;
    }

    modifier isBincentiveCold() {
        require(msg.sender == bincentiveCold);
        _;
    }

    modifier isInvestor() {
        // bincentive is not investor
        require(msg.sender != bincentiveHot);
        require(msg.sender != bincentiveCold);
        require(balances[msg.sender] > 0);
        _;
    }


    // Transfer functions for USDT
    function checkBalanceTransfer(address to, uint256 amount) internal {
        uint256 balanceBeforeTransfer = StableToken.balanceOf(to);
        uint256 balanceAfterTransfer;
        StableToken.transfer(to, amount);
        balanceAfterTransfer = StableToken.balanceOf(to);
        require(balanceAfterTransfer == balanceBeforeTransfer.add(amount));
    }

    function checkBalanceTransferFrom(address from, address to, uint256 amount) internal {
        uint256 balanceBeforeTransfer = StableToken.balanceOf(to);
        uint256 balanceAfterTransfer;
        StableToken.transferFrom(from, to, amount);
        balanceAfterTransfer = StableToken.balanceOf(to);
        require(balanceAfterTransfer == balanceBeforeTransfer.add(amount));
    }


    // Getter Functions

    // Get the balance of an investor, denominated in stable token
    function getBalanceValue(address investor) public view returns(uint256) {
        uint256 accruedRatioUSDT = oracle.query();
        return balances[investor].mul(accruedRatioUSDT).div(baseRatio);
    }

    // Defi Functions

    function querycUSDTAmount() internal returns(uint256) {
        return cToken.balanceOf(address(this));
    }

    function querycExgRate() internal returns(uint256) {
        return cToken.exchangeRateCurrent();
    }

    function mintcUSDT(uint USDTAmount) public isBincentive {

        StableToken.approve(address(cToken), USDTAmount); // approve the transfer
        assert(cToken.mint(USDTAmount) == 0);

        emit MintcUSDT(USDTAmount);
    }

    function redeemcUSDT(uint RedeemcUSDTAmount) public isBincentive {

        require(cToken.redeem(RedeemcUSDTAmount) == 0, "something went wrong");

        emit RedeemcUSDT(RedeemcUSDTAmount);
    }


    // Investor Deposit
    // It can either be called by investor directly or by bincentive accounts.
    // Only the passed in argument `investor` would be treated as the real investor.
    function deposit(address investor, uint256 depositUSDTAmount, bytes32 uuid, string referralCode) initialized public {
        require(now < investPaymentDueTime, "Deposit too late");
        require((investor != bincentiveHot) && (investor != bincentiveCold), "Investor can not be bincentive accounts");
        require(depositUSDTAmount > 0, "Deposited stable token amount should be greater than zero");

        // Transfer Stable Token to this contract
        checkBalanceTransferFrom(msg.sender, address(this), depositUSDTAmount);

        // Add investor to investor list if not present in the record before
        if(isInInvestorList[investor] == false) {
            investors.push(investor);
            isInInvestorList[investor] = true;
        }
        currentInvestedAmount = currentInvestedAmount.add(depositUSDTAmount);
        investorDepositUSDTAmount[investor] = investorDepositUSDTAmount[investor].add(depositUSDTAmount);

        // Query Oracle for current stable token ratio
        uint256 accruedRatioUSDT = oracle.query();
        // Mint and distribute tokens to investors
        uint256 mintedTokenAmount;
        mintedTokenAmount = depositUSDTAmount.mul(baseRatio).div(accruedRatioUSDT);
        _mint(investor, mintedTokenAmount);

        emit Deposit(investor, depositUSDTAmount, mintedTokenAmount);
        emit UserInfo(uuid, referralCode);
    }

    // Start Investing
    // Send part of the funds offline
    // and calculate the minimum amount of fund needed to keep the fund functioning
    // and calculate the maximum amount of fund allowed to be withdrawn per period.
    function start() initialized isBincentive public {
        // Send some USDT offline
        uint256 amountSentOffline = currentInvestedAmount.mul(percentageOffchainFund).div(100);
        checkBalanceTransfer(bincentiveCold, amountSentOffline);

        minimumFund = totalSupply().mul(percentageMinimumFund).div(100);
        // Start the contract
        fundStatus = 4;
        emit StartFund(now, investors.length, currentInvestedAmount, totalSupply());
    }

    function amountWithdrawable() public view returns(uint256) {
        return totalSupply().sub(minimumFund);
    }

    function isAmountWithdrawable(address investor, uint256 tokenAmount) public view returns(bool) {
        require(tokenAmount > 0, "Withdrawn amount must be greater than zero");
        require(balances[investor] >= tokenAmount, "Not enough token to be withdrawn");
        require(totalSupply().sub(tokenAmount) >= minimumFund, "Amount of fund left would be less than minimum fund threshold after withdrawal");

        return true;
    }

    function withdraw(address investor, uint256 tokenAmount) running isBincentive public {
        require(tokenAmount > 0, "Withdrawn amount must be greater than zero");
        require(balances[investor] >= tokenAmount, "Not enough token to be withdrawn");
        require(totalSupply().sub(tokenAmount) >= minimumFund, "Amount of fund left would be less than minimum fund threshold after withdrawal");

        uint256 investorBalanceBeforeWithdraw = balances[investor];
        // Substract withdrawing amount from investor's balance
        _burn(investor, tokenAmount);

        uint256 depositUSDTAmount = investorDepositUSDTAmount[investor];

        // Query Oracle for current stable token ratio
        uint256 accruedRatioUSDT = oracle.query();
        uint256 principle;
        uint256 interest;
        uint256 amountUSDTToWithdraw;
        uint256 amountUSDTForInvestor;
        uint256 amountUSDTToBincentiveCold;

        amountUSDTToWithdraw = tokenAmount.mul(accruedRatioUSDT).div(baseRatio);
        principle = depositUSDTAmount.mul(tokenAmount).div(investorBalanceBeforeWithdraw);
        interest = amountUSDTToWithdraw.sub(principle);
        amountUSDTForInvestor = principle.mul(99).div(100).add(interest.div(2));
        amountUSDTToBincentiveCold = amountUSDTToWithdraw.sub(amountUSDTForInvestor);

        // Check if `amountUSDTToBincentiveCold >= penalty`
        if (amountUSDTToBincentiveCold < minPenalty) {
            uint256 dif = minPenalty.sub(amountUSDTToBincentiveCold);
            require(dif <= amountUSDTForInvestor, "Withdraw amount is not enough to cover minimum penalty");
            amountUSDTForInvestor = amountUSDTForInvestor.sub(dif);
            amountUSDTToBincentiveCold = minPenalty;
        }

        investorDepositUSDTAmount[investor] = investorDepositUSDTAmount[investor].sub(principle);

        checkBalanceTransfer(investor, amountUSDTForInvestor);
        checkBalanceTransfer(bincentiveCold, amountUSDTToBincentiveCold);

        emit Withdraw(investor, tokenAmount, amountUSDTForInvestor, amountUSDTToBincentiveCold);

        // Suspend the contract if not enough fund remained
        if(totalSupply() == minimumFund) {
            fundStatus = 7;
        }
    }

    // Return AUM
    // Transfer the fund back to the contract
    function returnAUM(uint256 stableTokenAmount) runningOrSuspended isBincentiveCold public {
        // Option 1: contract transfer AUM directly from bincentiveCold
        checkBalanceTransferFrom(bincentiveCold, address(this), stableTokenAmount);

        emit ReturnAUM(stableTokenAmount);

        // If fund is running, stop the fund after AUM is returned
        if(fundStatus == 4) fundStatus = 5;
    }

    // Add an overlay on top of underlying token transfer
    // because token receiver should also be added to investor list to be able to receive AUM.
    function transfer(address _to, uint256 _value) public returns (bool){
        uint256 tokenBalanceBeforeTransfer = balances[msg.sender];
        bool success = super.transfer(_to, _value);

        if(success == true) {
            if(isInInvestorList[_to] == false) {
                investors.push(_to);
                isInInvestorList[_to] = true;
            }
            // Also transfer the deposited USDT so the receiver can withdraw
            uint256 USDTAmountToTransfer = investorDepositUSDTAmount[msg.sender].mul(_value).div(tokenBalanceBeforeTransfer);
            investorDepositUSDTAmount[msg.sender] = investorDepositUSDTAmount[msg.sender].sub(USDTAmountToTransfer);
            investorDepositUSDTAmount[_to] = investorDepositUSDTAmount[_to].add(USDTAmountToTransfer);
        }
        return success;
    }

    // Add an overlay on top of underlying token transferFrom
    // because token receiver should also be added to investor list to be able to receive AUM.
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool){
        uint256 tokenBalanceBeforeTransfer = balances[_from];
        bool success = super.transferFrom(_from, _to, _value);

        if(success == true) {
            if(isInInvestorList[_to] == false) {
                investors.push(_to);
                isInInvestorList[_to] = true;
            }
            // Also transfer the deposited USDT so the receiver can withdraw
            uint256 USDTAmountToTransfer = investorDepositUSDTAmount[_from].mul(_value).div(tokenBalanceBeforeTransfer);
            investorDepositUSDTAmount[_from] = investorDepositUSDTAmount[_from].sub(USDTAmountToTransfer);
            investorDepositUSDTAmount[_to] = investorDepositUSDTAmount[_to].add(USDTAmountToTransfer);
        }
        return success;
    }

    function update_investor(address _old_address, address _new_address) public isBincentiveCold {
        require((_new_address != bincentiveHot) && (_new_address != bincentiveCold), "Investor can not be bincentive accounts");
        require(isInInvestorList[_old_address] == true, "Investor does not exist");

        uint256 balance = balances[_old_address];
        balances[_old_address] = balances[_old_address].sub(balance);
        balances[_new_address] = balances[_new_address].add(balance);
        emit Transfer(_old_address, _new_address, balance);
        if(isInInvestorList[_new_address] == false) {
            investors.push(_new_address);
            isInInvestorList[_new_address] = true;
        }
        uint256 USDTAmountToTransfer = investorDepositUSDTAmount[_old_address];
        investorDepositUSDTAmount[_old_address] = investorDepositUSDTAmount[_old_address].sub(USDTAmountToTransfer);
        investorDepositUSDTAmount[_new_address] = investorDepositUSDTAmount[_new_address].add(USDTAmountToTransfer);
    }

    // Distribute AUM
    // Dispense the fund returned to each investor according to his portion of the token he possessed.
    // All withdraw requests should be processed before calling this function.
    // Since there might be too many investors, each time this function is called,
    // a parameter `numInvestorsToDistribute` is passed in to indicate how many investors to process this time.
    function distributeAUM(uint256 numInvestorsToDistribute) stoppedOrSuspended isBincentive public {
        require(numAUMDistributedInvestors.add(numInvestorsToDistribute) <= investors.length, "Distributing to more than total number of investors");

        // Query Oracle for current stable token ratio
        uint256 accruedRatioUSDT = oracle.query();

        uint256 stableTokenDistributeAmount;
        address investor;
        uint256 investor_amount;
        // Distribute Stable Token to investors
        for(uint i = numAUMDistributedInvestors; i < (numAUMDistributedInvestors.add(numInvestorsToDistribute)); i++) {
            investor = investors[i];
            investor_amount = balances[investor];
            if(investor_amount == 0) continue;
            _burn(investor, investor_amount);

            stableTokenDistributeAmount = investor_amount.mul(accruedRatioUSDT).div(baseRatio);
            checkBalanceTransfer(investor, stableTokenDistributeAmount);

            emit DistributeAUM(investor, investor_amount, stableTokenDistributeAmount);
        }

        numAUMDistributedInvestors = numAUMDistributedInvestors.add(numInvestorsToDistribute);
        // If all investors have received AUM, then close the fund.
        if(numAUMDistributedInvestors >= investors.length) {
            currentInvestedAmount = 0;
            // If fund is stopped, close the fund
            if(fundStatus == 5) fundStatus = 6;
        }
    }

    function claimWronglyTransferredFund() closedOrAbortedOrSuspended isBincentive public {
        // withdraw leftover funds from Defi
        uint256 totalcUSDTAmount;
        totalcUSDTAmount = querycUSDTAmount();
        redeemcUSDT(totalcUSDTAmount);

        uint256 leftOverAmount = StableToken.balanceOf(address(this));
        if(leftOverAmount > 0) {
            checkBalanceTransfer(bincentiveCold, leftOverAmount);
        }
    }

    function updateBincentiveColdAddress(address _newBincentiveCold) public isBincentiveCold {
        require(_newBincentiveCold != address(0), "New BincentiveCold address can not be zero");

        bincentiveCold = _newBincentiveCold;
        emit NewBincentiveCold(_newBincentiveCold);
    }

    constructor(
        address _oracle,
        address _StableToken,
        address _cToken,
        address _bincentiveHot,
        address _bincentiveCold,
        uint256 _investPaymentPeriod,
        uint256 _percentageOffchainFund,
        uint256 _percentageMinimumFund) WhiteListToken(_bincentiveCold) public {

        oracle = SimpleOracleAccruedRatioUSD(_oracle);
        bincentiveHot = _bincentiveHot;
        bincentiveCold = _bincentiveCold;
        StableToken = ITetherERC20(_StableToken);
        cToken = CERC20(_cToken);

        decimals = StableToken.decimals();
        minPenalty = 100 * (10 ** uint256(decimals));  // a minimum 100 USDT penalty
        baseRatio = oracle.query();
        require(baseRatio > 0, "baseRatio should always greater than zero");

        // Set parameters
        investPaymentDueTime = now.add(_investPaymentPeriod);
        percentageOffchainFund = _percentageOffchainFund;
        percentageMinimumFund = _percentageMinimumFund;

        // Initialized the contract
        fundStatus = 1;
    }
}
Settings
{
  "compilationTarget": {
    "CoolBitETFUSDTAndCompound.sol": "CoolBitETFUSDTAndCompound"
  },
  "evmVersion": "byzantium",
  "libraries": {},
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"transferWhiteList","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"bincentiveHot","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minPenalty","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"receiveWhiteList","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"percentageOffchainFund","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"amountWithdrawable","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_whiteListAddress","type":"address"}],"name":"removeReceiveWhiteListAddress","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"enableReceive","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"investors","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"investorDepositUSDTAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"StableToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"investor","type":"address"}],"name":"getBalanceValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isTransferRestricted","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"minimumFund","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_old_address","type":"address"},{"name":"_new_address","type":"address"}],"name":"update_investor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"cToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"whiteListAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"investor","type":"address"},{"name":"depositUSDTAmount","type":"uint256"},{"name":"uuid","type":"bytes32"},{"name":"referralCode","type":"string"}],"name":"deposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"numInvestorsToDistribute","type":"uint256"}],"name":"distributeAUM","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"investPaymentDueTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oracle","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"stableTokenAmount","type":"uint256"}],"name":"returnAUM","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isInInvestorList","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"baseRatio","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"investor","type":"address"},{"name":"tokenAmount","type":"uint256"}],"name":"isAmountWithdrawable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"RedeemcUSDTAmount","type":"uint256"}],"name":"redeemcUSDT","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"start","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimWronglyTransferredFund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isReceiveRestricted","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_whiteListAddress","type":"address"}],"name":"removeTransferWhiteListAddress","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"USDTAmount","type":"uint256"}],"name":"mintcUSDT","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"restrictTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"bincentiveCold","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_whiteListAddress","type":"address"}],"name":"addTransferWhiteListAddress","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_whiteListAddress","type":"address"}],"name":"addReceiveWhiteListAddress","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"numAUMDistributedInvestors","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fundStatus","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newBincentiveCold","type":"address"}],"name":"updateBincentiveColdAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"restrictReceive","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"enableTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"investor","type":"address"},{"name":"tokenAmount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"percentageMinimumFund","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentInvestedAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_oracle","type":"address"},{"name":"_StableToken","type":"address"},{"name":"_cToken","type":"address"},{"name":"_bincentiveHot","type":"address"},{"name":"_bincentiveCold","type":"address"},{"name":"_investPaymentPeriod","type":"uint256"},{"name":"_percentageOffchainFund","type":"uint256"},{"name":"_percentageMinimumFund","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"investor","type":"address"},{"indexed":false,"name":"investAmount","type":"uint256"},{"indexed":false,"name":"mintedAmount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"uuid","type":"bytes32"},{"indexed":false,"name":"referralCode","type":"string"}],"name":"UserInfo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"timeStamp","type":"uint256"},{"indexed":false,"name":"num_investors","type":"uint256"},{"indexed":false,"name":"totalInvestedAmount","type":"uint256"},{"indexed":false,"name":"totalMintedTokenAmount","type":"uint256"}],"name":"StartFund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"investor","type":"address"},{"indexed":false,"name":"tokenAmount","type":"uint256"},{"indexed":false,"name":"USDTAmount","type":"uint256"},{"indexed":false,"name":"ToBincentiveColdUSDTAmount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"investor","type":"address"},{"indexed":false,"name":"tokenAmount","type":"uint256"},{"indexed":false,"name":"USDTAmount","type":"uint256"}],"name":"MidwayQuit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"StableTokenAmount","type":"uint256"}],"name":"ReturnAUM","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"tokenAmount","type":"uint256"},{"indexed":false,"name":"StableTokenAmount","type":"uint256"}],"name":"DistributeAUM","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newBincentiveCold","type":"address"}],"name":"NewBincentiveCold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"USDTAmount","type":"uint256"}],"name":"MintcUSDT","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"RedeemcUSDTAmount","type":"uint256"}],"name":"RedeemcUSDT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]