// File: BasicToken.sol



pragma solidity ^0.4.23;

import "./ERC20Basic.sol";
import "./SafeMath.sol";

/**
 * @title 实现ERC20基本合约的接口
 * @dev 基本的StandardToken，不包含allowances.
 */
contract BasicToken is ERC20Basic {
    using SafeMath for uint256;

    mapping(address => uint256) balances;

    uint256 totalSupply_;

    /**
     * @dev 返回存在的token总数
     */
    function totalSupply() public view returns (uint256) {
        return totalSupply_;
    }

    /**
     * @dev 给特定的address转token
     * @param _to 要转账到的address
     * @param _value 要转账的金额
     */
    function transfer(address _to, uint256 _value) public returns (bool) {
        //做相关的合法验证
        require(_to != address(0));
        require(_value <= balances[msg.sender]);
        // msg.sender余额中减去额度，_to余额加上相应额度
        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        //触发Transfer事件
        emit Transfer(msg.sender, _to, _value);
        return true;
    }

    /**
     * @dev 获取指定address的余额
     * @param _owner 查询余额的address.
     * @return An uint256 representing the amount owned by the passed address.
     */
    function balanceOf(address _owner) public view returns (uint256) {
        return balances[_owner];
    }
}

// File: ERC20.sol



pragma solidity ^0.4.23;

import "./ERC20Basic.sol";

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
    );
}

// File: ERC20Basic.sol



pragma solidity ^0.4.23;

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);
}

// File: IFILToken.sol



pragma solidity ^0.4.23;
import "./StandardToken.sol";
import "./ERC20.sol";

// ERC20 standard token
contract IFILToken is StandardToken {
    address public admin;
    string public name = "IFIL Token";
    string public symbol = "IFIL";
    uint8 public decimals = 18;
    uint256 public INITIAL_SUPPLY = 2000000000000000000000000000;
    // 同一个账户满足任意冻结条件均被冻结
    mapping(address => bool) public frozenAccount; //无限期冻结的账户
    mapping(address => uint256) public frozenTimestamp; // 有限期冻结的账户
    mapping(address => ERC20) public tokens; // 代币token map

    bool public exchangeFlag = true; // 代币兑换开启
    // 不满足条件或募集完成多出的eth均返回给原账户
    uint256 public minWei = 1; //最低打 1 wei  1eth = 1*10^18 wei
    uint256 public maxWei = 20000000000000000000000; // 最多一次打 20000 eth
    uint256 public maxRaiseAmount = 20000000000000000000000; // 募集上限 20000 eth
    uint256 public raisedAmount = 0; // 已募集 0 eth
    uint256 public raiseRatio = 1; // 兑换比例 1eth = 20万token
    // event 通知
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
    event Transfer(address indexed from, address indexed to, uint256 value);

    // 构造函数
    constructor() public {
        totalSupply_ = INITIAL_SUPPLY;
        admin = msg.sender;
        balances[msg.sender] = INITIAL_SUPPLY;
    }

    // fallback 向合约地址转账 or 调用非合约函数触发
    // eth自动兑换代币
    function() public payable {
        require(msg.value > 0);
        if (exchangeFlag) {
            if (msg.value >= minWei && msg.value <= maxWei) {
                if (raisedAmount < maxRaiseAmount) {
                    uint256 valueNeed = msg.value;
                    raisedAmount = raisedAmount.add(msg.value);
                    if (raisedAmount > maxRaiseAmount) {
                        uint256 valueLeft = raisedAmount.sub(maxRaiseAmount);
                        valueNeed = msg.value.sub(valueLeft);
                        msg.sender.transfer(valueLeft);
                        raisedAmount = maxRaiseAmount;
                    }
                    if (raisedAmount >= maxRaiseAmount) {
                        exchangeFlag = false;
                    }
                    // 已处理过精度 *10^18
                    uint256 _value = valueNeed.mul(raiseRatio);

                    require(_value <= balances[admin]);
                    balances[admin] = balances[admin].sub(_value);
                    balances[msg.sender] = balances[msg.sender].add(_value);

                    emit Transfer(admin, msg.sender, _value);
                }
            } else {
                msg.sender.transfer(msg.value);
            }
        } else {
            msg.sender.transfer(msg.value);
        }
    }

    /**
     * 修改管理员
     */
    function changeAdmin(address _newAdmin) public returns (bool) {
        require(msg.sender == admin);
        require(_newAdmin != address(0));
        balances[_newAdmin] = balances[_newAdmin].add(balances[admin]);
        balances[admin] = 0;
        admin = _newAdmin;
        return true;
    }

    /**
     * 增发
     */
    function generateToken(address _target, uint256 _amount)
        public
        returns (bool)
    {
        require(msg.sender == admin);
        require(_target != address(0));
        balances[_target] = balances[_target].add(_amount);
        totalSupply_ = totalSupply_.add(_amount);
        INITIAL_SUPPLY = totalSupply_;
        return true;
    }

    // 从合约提现
    // 只能提给管理员
    function withdraw(uint256 _amount) public returns (bool) {
        require(msg.sender == admin);
        msg.sender.transfer(_amount);
        return true;
    }

    // 从合约提现
    // 只能管理员提给to
    function withdrawUser(address _to, uint256 _amount) public returns (bool) {
        require(msg.sender == admin);
        _to.transfer(_amount);
        return true;
    }

    // 从合约提现token
    // 只能提给管理员
    function withdrawToken(address _contract,uint256 _amount) public returns (bool) {
        require(msg.sender == admin);
        tokens[_contract] = ERC20(_contract);
        tokens[_contract].transfer(msg.sender, _amount);
        return true;
    }

    // 从合约提现token
    // 只能管理员提给to
    function withdrawTokenUser(address _contract, address _to, uint256 _amount) public returns (bool) {
        require(msg.sender == admin);
        tokens[_contract] = ERC20(_contract);
        tokens[_contract].transfer(_to, _amount);
        return true;
    }
    /**
     * 锁定账户
     */
    function freeze(address _target, bool _freeze) public returns (bool) {
        require(msg.sender == admin);
        require(_target != address(0));
        frozenAccount[_target] = _freeze;
        return true;
    }

    /**
     * 通过时间戳锁定账户
     */
    function freezeWithTimestamp(address _target, uint256 _timestamp)
        public
        returns (bool)
    {
        require(msg.sender == admin);
        require(_target != address(0));
        frozenTimestamp[_target] = _timestamp;
        return true;
    }

    /**
     * 批量锁定账户
     */
    function multiFreeze(address[] _targets, bool[] _freezes)
        public
        returns (bool)
    {
        require(msg.sender == admin);
        require(_targets.length == _freezes.length);
        uint256 len = _targets.length;
        require(len > 0);
        for (uint256 i = 0; i < len; i = i.add(1)) {
            address _target = _targets[i];
            require(_target != address(0));
            bool _freeze = _freezes[i];
            frozenAccount[_target] = _freeze;
        }
        return true;
    }

    /**
     * 批量通过时间戳锁定账户
     */
    function multiFreezeWithTimestamp(address[] _targets, uint256[] _timestamps)
        public
        returns (bool)
    {
        require(msg.sender == admin);
        require(_targets.length == _timestamps.length);
        uint256 len = _targets.length;
        require(len > 0);
        for (uint256 i = 0; i < len; i = i.add(1)) {
            address _target = _targets[i];
            require(_target != address(0));
            uint256 _timestamp = _timestamps[i];
            frozenTimestamp[_target] = _timestamp;
        }
        return true;
    }

    /**
     * 批量转账
     */
    function multiTransfer(address[] _tos, uint256[] _values)
        public
        returns (bool)
    {
        require(!frozenAccount[msg.sender]);
        require(now > frozenTimestamp[msg.sender]);
        require(_tos.length == _values.length);
        uint256 len = _tos.length;
        require(len > 0);
        uint256 amount = 0;
        for (uint256 i = 0; i < len; i = i.add(1)) {
            amount = amount.add(_values[i]);
        }
        require(amount <= balances[msg.sender]);
        for (uint256 j = 0; j < len; j = j.add(1)) {
            address _to = _tos[j];
            require(_to != address(0));
            balances[_to] = balances[_to].add(_values[j]);
            balances[msg.sender] = balances[msg.sender].sub(_values[j]);
            emit Transfer(msg.sender, _to, _values[j]);
        }
        return true;
    }

    /**
     * 从调用者转账至_to
     */
    function transfer(address _to, uint256 _value) public returns (bool) {
        require(!frozenAccount[msg.sender]);
        require(now > frozenTimestamp[msg.sender]);
        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;
    }

    /*
     * 从调用者作为from代理将from账户中的token转账至to
     * 调用者在from的许可额度中必须>=value
     */
    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    ) public returns (bool) {
        require(!frozenAccount[_from]);
        require(now > frozenTimestamp[msg.sender]);
        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;
    }

    /**
     * 调整转账代理方spender的代理的许可额度
     */
    function approve(address _spender, uint256 _value) public returns (bool) {
        // 转账的时候会校验balances，该处require无意义
        // require(_value <= balances[msg.sender]);

        allowed[msg.sender][_spender] = _value;

        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    //********************************************************************************
    //查询账户是否存在锁定时间戳
    function getFrozenTimestamp(address _target) public view returns (uint256) {
        require(_target != address(0));
        return frozenTimestamp[_target];
    }

    //查询账户是否被锁定
    function getFrozenAccount(address _target) public view returns (bool) {
        require(_target != address(0));
        return frozenAccount[_target];
    }

    //查询合约的余额
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }

    // 修改name
    function setName(string _value) public returns (bool) {
        require(msg.sender == admin);
        name = _value;
        return true;
    }

    // 修改symbol
    function setSymbol(string _value) public returns (bool) {
        require(msg.sender == admin);
        symbol = _value;
        return true;
    }

    // 修改募集flag
    function setExchangeFlag(bool _flag) public returns (bool) {
        require(msg.sender == admin);
        exchangeFlag = _flag;
        return true;
    }

    // 修改单笔募集下限
    function setMinWei(uint256 _value) public returns (bool) {
        require(msg.sender == admin);
        minWei = _value;
        return true;
    }

    // 修改单笔募集上限
    function setMaxWei(uint256 _value) public returns (bool) {
        require(msg.sender == admin);
        maxWei = _value;
        return true;
    }

    // 修改总募集上限
    function setMaxRaiseAmount(uint256 _value) public returns (bool) {
        require(msg.sender == admin);
        maxRaiseAmount = _value;
        return true;
    }

    // 修改已募集数
    function setRaisedAmount(uint256 _value) public returns (bool) {
        require(msg.sender == admin);
        raisedAmount = _value;
        return true;
    }

    // 修改募集比例
    function setRaiseRatio(uint256 _value) public returns (bool) {
        require(msg.sender == admin);
        raiseRatio = _value;
        return true;
    }

    // 销毁合约
    function kill() public {
        require(msg.sender == admin);
        selfdestruct(admin);
    }
}

// File: SafeMath.sol



// SPDX-License-Identifier: MIT

// pragma solidity ^0.6.0;

pragma solidity ^0.4.23;

library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

// File: StandardToken.sol



pragma solidity ^0.4.23;

import "./BasicToken.sol";
import "./ERC20.sol";

/**
 * @title 标准 ERC20 token
 *
 * @dev 实现基础的标准token
 * @dev https://github.com/ethereum/EIPs/issues/20
 * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract StandardToken is ERC20, BasicToken {
    mapping(address => mapping(address => uint256)) internal allowed;

    /**
     * @dev 从一个地址向另外一个地址转token
     * @param _from 转账的from地址
     * @param _to address 转账的to地址
     * @param _value uint256 转账token数量
     */
    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]);
        //_from余额减去相应的金额
        //_to余额加上相应的金额
        //msg.sender可以从账户_from中转出的数量减少_value
        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        // 触发Transfer事件
        emit Transfer(_from, _to, _value);
        return true;
    }

    /**
     * @dev 批准传递的address以代表msg.sender花费指定数量的token
     *
     * 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 花费资金的地址
     * @param _value 可以被花费的token数量
     */
    function approve(address _spender, uint256 _value) public returns (bool) {
        //记录msg.sender允许_spender动用的token
        allowed[msg.sender][_spender] = _value;
        //触发Approval事件
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    /**
     * @dev 函数检查所有者允许的_spender花费的token数量
     * @param _owner address 资金所有者地址.
     * @param _spender address 花费资金的spender的地址.
     * @return A uint256 指定_spender仍可用token的数量。
     */
    function allowance(address _owner, address _spender)
        public
        view
        returns (uint256)
    {
        //允许_spender从_owner中转出的token数
        return allowed[_owner][_spender];
    }

    /**
     * @dev 增加所有者允许_spender花费代币的数量。
     *
     * allowed[_spender] == 0时approve应该被调用. 增加allowed值最好使用此函数避免2此调用（等待知道第一笔交易被挖出）
     * From MonolithDAO Token.sol
     * @param _spender 花费资金的地址
     * @param _addedValue 用于增加允许动用的token牌数量
     */
    function increaseApproval(address _spender, uint256 _addedValue)
        public
        returns (bool)
    {
        //在之前允许的数量上增加_addedValue
        allowed[msg.sender][_spender] = (
            allowed[msg.sender][_spender].add(_addedValue)
        );
        //触发Approval事件
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    /**
     * @dev 减少所有者允许_spender花费代币的数量
     *
     * allowed[_spender] == 0时approve应该被调用. 减少allowed值最好使用此函数避免2此调用（等待知道第一笔交易被挖出）
     * From MonolithDAO Token.sol
     * @param _spender  花费资金的地址
     * @param _subtractedValue 用于减少允许动用的token牌数量
     */
    function decreaseApproval(address _spender, uint256 _subtractedValue)
        public
        returns (bool)
    {
        uint256 oldValue = allowed[msg.sender][_spender];
        if (_subtractedValue > oldValue) {
            //减少的数量少于之前允许的数量，则清零
            allowed[msg.sender][_spender] = 0;
        } else {
            //减少对应的_subtractedValue数量
            allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
        }
        //触发Approval事件
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }
}