ETH Price: $3,221.08 (-3.72%)
Gas: 4 Gwei

Contract Diff Checker

Contract Name:
ApisToken

Contract Source Code:

File 1 of 1 : ApisToken

pragma solidity ^0.4.18;

/**
 * @title ERC20Basic
 * @dev Simpler version of ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/179
 */
contract ERC20Basic {
  uint256 public totalSupply;
  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 SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

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

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}


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

  mapping(address => uint256) balances;

  /**
  * @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]);

    // SafeMath.sub will throw if there is not enough balance.
    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    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 balance) {
    return balances[_owner];
  }

}


/**
 * @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 Standard ERC20 token
 *
 * @dev Implementation of the basic standard 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 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);
    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;
    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, uint _addedValue) public returns (bool) {
    allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
    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, uint _subtractedValue) public returns (bool) {
    uint oldValue = allowed[msg.sender][_spender];
    if (_subtractedValue > oldValue) {
      allowed[msg.sender][_spender] = 0;
    } else {
      allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
    }
    Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

}


/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
    address public owner;
    address public newOwner;


    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);


    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    function Ownable() public {
        owner = msg.sender;
    }


    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }


    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param _newOwner The address to transfer ownership to.
     */
    function transferOwnership(address _newOwner) public onlyOwner {
        require(_newOwner != address(0));
        OwnershipTransferred(owner, _newOwner);
        newOwner = _newOwner;
    }

    /**
     * @dev μƒˆλ‘œμš΄ κ΄€λ¦¬μžκ°€ μŠΉμΈν•΄μ•Όλ§Œ μ†Œμœ κΆŒμ΄ μ΄μ „λœλ‹€
     */
    function acceptOwnership() public {
        require(msg.sender == newOwner);
        
        OwnershipTransferred(owner, newOwner);
        owner = newOwner;
        newOwner = address(0);
    }
}


/**
 * @title APIS Token
 * @dev APIS 토큰을 μƒμ„±ν•œλ‹€
 */
contract ApisToken is StandardToken, Ownable {
    // ν† ν°μ˜ 이름
    string public constant name = "APIS";
    
    // ν† ν°μ˜ λ‹¨μœ„
    string public constant symbol = "APIS";
    
    // μ†Œμˆ˜μ  자리수. ETH 18μžλ¦¬μ— λ§žμΆ˜λ‹€
    uint8 public constant decimals = 18;
    
    // μ§€κ°‘λ³„λ‘œ μ†‘κΈˆ/수금 κΈ°λŠ₯의 잠긴 μ—¬λΆ€λ₯Ό μ €μž₯
    mapping (address => LockedInfo) public lockedWalletInfo;
    
    /**
     * @dev ν”Œλž«νΌμ—μ„œ μš΄μ˜ν•˜λŠ” λ§ˆμŠ€ν„°λ…Έλ“œ 슀마트 μ»¨νŠΈλ ‰νŠΈ μ£Όμ†Œ
     */
    mapping (address => bool) public manoContracts;
    
    
    /**
     * @dev 토큰 μ§€κ°‘μ˜ μž κΉ€ 속성을 μ •μ˜
     * 
     * @param timeLockUpEnd timeLockUpEnd μ‹œκ°„κΉŒμ§€ 솑/μˆ˜κΈˆμ— λŒ€ν•œ μ œν•œμ΄ μ μš©λœλ‹€. μ΄ν›„μ—λŠ” μ œν•œμ΄ ν’€λ¦°λ‹€
     * @param sendLock 좜금 μž κΉ€ μ—¬λΆ€(true : μž κΉ€, false : ν’€λ¦Ό)
     * @param receiveLock μž…κΈˆ μž κΉ€ μ—¬λΆ€ (true : μž κΉ€, false : ν’€λ¦Ό)
     */
    struct LockedInfo {
        uint timeLockUpEnd;
        bool sendLock;
        bool receiveLock;
    } 
    
    
    /**
     * @dev 토큰이 μ†‘κΈˆλμ„ λ•Œ λ°œμƒν•˜λŠ” 이벀트
     * @param from 토큰을 λ³΄λ‚΄λŠ” 지갑 μ£Όμ†Œ
     * @param to 토큰을 λ°›λŠ” 지갑 μ£Όμ†Œ
     * @param value μ „λ‹¬λ˜λŠ” ν† ν°μ˜ μ–‘ (Satoshi)
     */
    event Transfer (address indexed from, address indexed to, uint256 value);
    
    /**
     * @dev 토큰 μ§€κ°‘μ˜ μ†‘κΈˆ/μž…κΈˆ κΈ°λŠ₯이 μ œν•œλ˜μ—ˆμ„ λ•Œ λ°œμƒν•˜λŠ” 이벀트
     * @param target μ œν•œ λŒ€μƒ 지갑 μ£Όμ†Œ
     * @param timeLockUpEnd μ œν•œμ΄ μ’…λ£Œλ˜λŠ” μ‹œκ°„(UnixTimestamp)
     * @param sendLock μ§€κ°‘μ—μ„œμ˜ μ†‘κΈˆμ„ μ œν•œν•˜λŠ”μ§€ μ—¬λΆ€(true : μ œν•œ, false : ν•΄μ œ)
     * @param receiveLock μ§€κ°‘μœΌλ‘œμ˜ μž…κΈˆμ„ μ œν•œν•˜λŠ”μ§€ μ—¬λΆ€ (true : μ œν•œ, false : ν•΄μ œ)
     */
    event Locked (address indexed target, uint timeLockUpEnd, bool sendLock, bool receiveLock);
    
    /**
     * @dev 지갑에 λŒ€ν•œ μ†‘κΈˆ/μž…κΈˆ μ œν•œμ„ ν•΄μ œν–ˆμ„ λ•Œ λ°œμƒν•˜λŠ” 이벀트
     * @param target ν•΄μ œ λŒ€μƒ 지갑 μ£Όμ†Œ
     */
    event Unlocked (address indexed target);
    
    /**
     * @dev μ†‘κΈˆ λ°›λŠ” μ§€κ°‘μ˜ μž…κΈˆμ΄ μ œν•œλ˜μ–΄μžˆμ–΄μ„œ μ†‘κΈˆμ΄ κ±°μ ˆλ˜μ—ˆμ„ λ•Œ λ°œμƒν•˜λŠ” 이벀트
     * @param from 토큰을 λ³΄λ‚΄λŠ” 지갑 μ£Όμ†Œ
     * @param to (μž…κΈˆμ΄ μ œν•œλœ) 토큰을 λ°›λŠ” 지갑 μ£Όμ†Œ
     * @param value μ „μ†‘ν•˜λ €κ³  ν•œ ν† ν°μ˜ μ–‘(Satoshi)
     */
    event RejectedPaymentToLockedUpWallet (address indexed from, address indexed to, uint256 value);
    
    /**
     * @dev μ†‘κΈˆν•˜λŠ” μ§€κ°‘μ˜ 좜금이 μ œν•œλ˜μ–΄μžˆμ–΄μ„œ μ†‘κΈˆμ΄ κ±°μ ˆλ˜μ—ˆμ„ λ•Œ λ°œμƒν•˜λŠ” 이벀트
     * @param from (좜금이 μ œν•œλœ) 토큰을 λ³΄λ‚΄λŠ” 지갑 μ£Όμ†Œ
     * @param to 토큰을 λ°›λŠ” 지갑 μ£Όμ†Œ
     * @param value μ „μ†‘ν•˜λ €κ³  ν•œ ν† ν°μ˜ μ–‘(Satoshi)
     */
    event RejectedPaymentFromLockedUpWallet (address indexed from, address indexed to, uint256 value);
    
    /**
     * @dev 토큰을 μ†Œκ°ν•œλ‹€. 
     * @param burner 토큰을 μ†Œκ°ν•˜λŠ” 지갑 μ£Όμ†Œ
     * @param value μ†Œκ°ν•˜λŠ” ν† ν°μ˜ μ–‘(Satoshi)
     */
    event Burn (address indexed burner, uint256 value);
    
    /**
     * @dev μ•„ν”ΌμŠ€ ν”Œλž«νΌμ— λ§ˆμŠ€ν„°λ…Έλ“œ 슀마트 μ»¨νŠΈλ ‰νŠΈκ°€ λ“±λ‘λ˜κ±°λ‚˜ ν•΄μ œλ  λ•Œ λ°œμƒν•˜λŠ” 이벀트
     */
    event ManoContractRegistered (address manoContract, bool registered);
    
    /**
     * @dev μ»¨νŠΈλž™νŠΈκ°€ 생성될 λ•Œ μ‹€ν–‰. μ»¨νŠΈλ ‰νŠΈ μ†Œμœ μž 지갑에 λͺ¨λ“  토큰을 ν• λ‹Ήν•œλ‹€.
     * λ°œν–‰λŸ‰μ΄λ‚˜ 이름은 μ†ŒμŠ€μ½”λ“œμ—μ„œ 확인할 수 μžˆλ„λ‘ λ³€κ²½ν•˜μ˜€μŒ
     */
    function ApisToken() public {
        // 총 APIS λ°œν–‰λŸ‰ (95μ–΅ 2천만)
        uint256 supplyApis = 9520000000;
        
        // wei λ‹¨μœ„λ‘œ 토큰 μ΄λŸ‰μ„ μƒμ„±ν•œλ‹€.
        totalSupply = supplyApis * 10 ** uint256(decimals);
        
        balances[msg.sender] = totalSupply;
        
        Transfer(0x0, msg.sender, totalSupply);
    }
    
    
    /**
     * @dev 지갑을 μ§€μ •λœ μ‹œκ°„κΉŒμ§€ μ œν•œμ‹œν‚€κ±°λ‚˜ ν•΄μ œμ‹œν‚¨λ‹€. μ œν•œ μ‹œκ°„μ΄ κ²½κ³Όν•˜λ©΄ λͺ¨λ“  μ œν•œμ΄ ν•΄μ œλœλ‹€.
     * @param _targetWallet μ œν•œμ„ μ μš©ν•  지갑 μ£Όμ†Œ
     * @param _timeLockEnd μ œν•œμ΄ μ’…λ£Œλ˜λŠ” μ‹œκ°„(UnixTimestamp)
     * @param _sendLock (true : μ§€κ°‘μ—μ„œ 토큰을 μΆœκΈˆν•˜λŠ” κΈ°λŠ₯을 μ œν•œν•œλ‹€.) (false : μ œν•œμ„ ν•΄μ œν•œλ‹€)
     * @param _receiveLock (true : μ§€κ°‘μœΌλ‘œ 토큰을 μž…κΈˆλ°›λŠ” κΈ°λŠ₯을 μ œν•œν•œλ‹€.) (false : μ œν•œμ„ ν•΄μ œν•œλ‹€)
     */
    function walletLock(address _targetWallet, uint _timeLockEnd, bool _sendLock, bool _receiveLock) onlyOwner public {
        require(_targetWallet != 0x0);
        
        // If all locks are unlocked, set the _timeLockEnd to zero.
        if(_sendLock == false && _receiveLock == false) {
            _timeLockEnd = 0;
        }
        
        lockedWalletInfo[_targetWallet].timeLockUpEnd = _timeLockEnd;
        lockedWalletInfo[_targetWallet].sendLock = _sendLock;
        lockedWalletInfo[_targetWallet].receiveLock = _receiveLock;
        
        if(_timeLockEnd > 0) {
            Locked(_targetWallet, _timeLockEnd, _sendLock, _receiveLock);
        } else {
            Unlocked(_targetWallet);
        }
    }
    
    /**
     * @dev μ§€κ°‘μ˜ μž…κΈ‰/μΆœκΈˆμ„ μ§€μ •λœ μ‹œκ°„κΉŒμ§€ μ œν•œμ‹œν‚¨λ‹€. μ œν•œ μ‹œκ°„μ΄ κ²½κ³Όν•˜λ©΄ λͺ¨λ“  μ œν•œμ΄ ν•΄μ œλœλ‹€.
     * @param _targetWallet μ œν•œμ„ μ μš©ν•  지갑 μ£Όμ†Œ
     * @param _timeLockUpEnd μ œν•œμ΄ μ’…λ£Œλ˜λŠ” μ‹œκ°„(UnixTimestamp)
     */
    function walletLockBoth(address _targetWallet, uint _timeLockUpEnd) onlyOwner public {
        walletLock(_targetWallet, _timeLockUpEnd, true, true);
    }
    
    /**
     * @dev μ§€κ°‘μ˜ μž…κΈ‰/μΆœκΈˆμ„ μ˜μ›νžˆ(33658-9-27 01:46:39+00) μ œν•œμ‹œν‚¨λ‹€.
     * @param _targetWallet μ œν•œμ„ μ μš©ν•  지갑 μ£Όμ†Œ
     */
    function walletLockBothForever(address _targetWallet) onlyOwner public {
        walletLock(_targetWallet, 999999999999, true, true);
    }
    
    
    /**
     * @dev 지갑에 μ„€μ •λœ μž…μΆœκΈˆ μ œν•œμ„ ν•΄μ œν•œλ‹€
     * @param _targetWallet μ œν•œμ„ ν•΄μ œν•˜κ³ μž ν•˜λŠ” 지갑 μ£Όμ†Œ
     */
    function walletUnlock(address _targetWallet) onlyOwner public {
        walletLock(_targetWallet, 0, false, false);
    }
    
    /**
     * @dev μ§€κ°‘μ˜ μ†‘κΈˆ κΈ°λŠ₯이 μ œν•œλ˜μ–΄μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.
     * @param _addr μ†‘κΈˆ μ œν•œ μ—¬λΆ€λ₯Ό ν™•μΈν•˜λ €λŠ” μ§€κ°‘μ˜ μ£Όμ†Œ
     * @return isSendLocked (true : μ œν•œλ˜μ–΄ 있음, 토큰을 보낼 수 μ—†μŒ) (false : μ œν•œ μ—†μŒ, 토큰을 보낼 수 있음)
     * @return until μž κ²¨μžˆλŠ” μ‹œκ°„, UnixTimestamp
     */
    function isWalletLocked_Send(address _addr) public constant returns (bool isSendLocked, uint until) {
        require(_addr != 0x0);
        
        isSendLocked = (lockedWalletInfo[_addr].timeLockUpEnd > now && lockedWalletInfo[_addr].sendLock == true);
        
        if(isSendLocked) {
            until = lockedWalletInfo[_addr].timeLockUpEnd;
        } else {
            until = 0;
        }
    }
    
    /**
     * @dev μ§€κ°‘μ˜ μž…κΈˆ κΈ°λŠ₯이 μ œν•œλ˜μ–΄μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.
     * @param _addr μž…κΈˆ μ œν•œ μ—¬λΆ€λ₯Ό ν™•μΈν•˜λ €λŠ” μ§€κ°‘μ˜ μ£Όμ†Œ
     * @return (true : μ œν•œλ˜μ–΄ 있음, 토큰을 받을 수 μ—†μŒ) (false : μ œν•œ μ—†μŒ, 토큰을 받을 수 있음)
     */
    function isWalletLocked_Receive(address _addr) public constant returns (bool isReceiveLocked, uint until) {
        require(_addr != 0x0);
        
        isReceiveLocked = (lockedWalletInfo[_addr].timeLockUpEnd > now && lockedWalletInfo[_addr].receiveLock == true);
        
        if(isReceiveLocked) {
            until = lockedWalletInfo[_addr].timeLockUpEnd;
        } else {
            until = 0;
        }
    }
    
    /**
     * @dev μš”μ²­μžμ˜ 지갑에 μ†‘κΈˆ κΈ°λŠ₯이 μ œν•œλ˜μ–΄μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.
     * @return (true : μ œν•œλ˜μ–΄ 있음, 토큰을 보낼 수 μ—†μŒ) (false : μ œν•œ μ—†μŒ, 토큰을 보낼 수 있음)
     */
    function isMyWalletLocked_Send() public constant returns (bool isSendLocked, uint until) {
        return isWalletLocked_Send(msg.sender);
    }
    
    /**
     * @dev μš”μ²­μžμ˜ 지갑에 μž…κΈˆ κΈ°λŠ₯이 μ œν•œλ˜μ–΄μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.
     * @return (true : μ œν•œλ˜μ–΄ 있음, 토큰을 보낼 수 μ—†μŒ) (false : μ œν•œ μ—†μŒ, 토큰을 보낼 수 있음)
     */
    function isMyWalletLocked_Receive() public constant returns (bool isReceiveLocked, uint until) {
        return isWalletLocked_Receive(msg.sender);
    }
    
    
    /**
     * @dev μ•„ν”ΌμŠ€ ν”Œλž«νΌμ—μ„œ μš΄μ˜ν•˜λŠ” 슀마트 μ»¨νŠΈλ ‰νŠΈ μ£Όμ†Œλ₯Ό λ“±λ‘ν•˜κ±°λ‚˜ ν•΄μ œν•œλ‹€.
     * @param manoAddr λ§ˆμŠ€ν„°λ…Έλ“œ 슀마트 μ»¨νŠΈλ ‰μ»¨νŠΈλ ‰νŠΈ
     * @param registered true : 등둝, false : ν•΄μ œ
     */
    function registerManoContract(address manoAddr, bool registered) onlyOwner public {
        manoContracts[manoAddr] = registered;
        
        ManoContractRegistered(manoAddr, registered);
    }
    
    
    /**
     * @dev _to μ§€κ°‘μœΌλ‘œ _apisWei 만큼의 토큰을 μ†‘κΈˆν•œλ‹€.
     * @param _to 토큰을 λ°›λŠ” 지갑 μ£Όμ†Œ
     * @param _apisWei μ „μ†‘λ˜λŠ” ν† ν°μ˜ μ–‘
     */
    function transfer(address _to, uint256 _apisWei) public returns (bool) {
        // μžμ‹ μ—κ²Œ μ†‘κΈˆν•˜λŠ” 것을 λ°©μ§€ν•œλ‹€
        require(_to != address(this));
        
        // λ§ˆμŠ€ν„°λ…Έλ“œ μ»¨νŠΈλ ‰νŠΈμΌ 경우, APIS μ†‘μˆ˜μ‹ μ— μ œν•œμ„ 두지 μ•ŠλŠ”λ‹€
        if(manoContracts[msg.sender] || manoContracts[_to]) {
            return super.transfer(_to, _apisWei);
        }
        
        // μ†‘κΈˆ κΈ°λŠ₯이 잠긴 지갑인지 ν™•μΈν•œλ‹€.
        if(lockedWalletInfo[msg.sender].timeLockUpEnd > now && lockedWalletInfo[msg.sender].sendLock == true) {
            RejectedPaymentFromLockedUpWallet(msg.sender, _to, _apisWei);
            return false;
        } 
        // μž…κΈˆ λ°›λŠ” κΈ°λŠ₯이 잠긴 지갑인지 ν™•μΈν•œλ‹€
        else if(lockedWalletInfo[_to].timeLockUpEnd > now && lockedWalletInfo[_to].receiveLock == true) {
            RejectedPaymentToLockedUpWallet(msg.sender, _to, _apisWei);
            return false;
        } 
        // μ œν•œμ΄ μ—†λŠ” 경우, μ†‘κΈˆμ„ μ§„ν–‰ν•œλ‹€.
        else {
            return super.transfer(_to, _apisWei);
        }
    }
    
    /**
     * @dev _to μ§€κ°‘μœΌλ‘œ _apisWei 만큼의 APISλ₯Ό μ†‘κΈˆν•˜κ³  _timeLockUpEnd μ‹œκ°„λ§ŒνΌ 지갑을 μž κ·Όλ‹€
     * @param _to 토큰을 λ°›λŠ” 지갑 μ£Όμ†Œ
     * @param _apisWei μ „μ†‘λ˜λŠ” ν† ν°μ˜ μ–‘(wei)
     * @param _timeLockUpEnd 잠금이 ν•΄μ œλ˜λŠ” μ‹œκ°„
     */
    function transferAndLockUntil(address _to, uint256 _apisWei, uint _timeLockUpEnd) onlyOwner public {
        require(transfer(_to, _apisWei));
        
        walletLockBoth(_to, _timeLockUpEnd);
    }
    
    /**
     * @dev _to μ§€κ°‘μœΌλ‘œ _apisWei 만큼의 APISλ₯Ό μ†‘κΈˆν•˜κ³ μ˜μ›νžˆ 지갑을 μž κ·Όλ‹€
     * @param _to 토큰을 λ°›λŠ” 지갑 μ£Όμ†Œ
     * @param _apisWei μ „μ†‘λ˜λŠ” ν† ν°μ˜ μ–‘(wei)
     */
    function transferAndLockForever(address _to, uint256 _apisWei) onlyOwner public {
        require(transfer(_to, _apisWei));
        
        walletLockBothForever(_to);
    }
    
    
    /**
     * @dev ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λŠ” μ§€κ°‘μ˜ 토큰을 μ†Œκ°ν•œλ‹€.
     * 
     * zeppelin-solidity/contracts/token/BurnableToken.sol μ°Έμ‘°
     * @param _value μ†Œκ°ν•˜λ €λŠ” ν† ν°μ˜ μ–‘(Satoshi)
     */
    function burn(uint256 _value) public {
        require(_value <= balances[msg.sender]);
        require(_value <= totalSupply);
        
        address burner = msg.sender;
        balances[burner] -= _value;
        totalSupply -= _value;
        
        Burn(burner, _value);
    }
    
    
    /**
     * @dev Eth은 받을 수 없도둝 ν•œλ‹€.
     */
    function () public payable {
        revert();
    }
}








/**
 * @title WhiteList
 * @dev ICO μ°Έμ—¬κ°€ κ°€λŠ₯ν•œ ν™”μ΄νŠΈ 리슀트λ₯Ό κ΄€λ¦¬ν•œλ‹€
 */
contract WhiteList is Ownable {
    
    mapping (address => uint8) internal list;
    
    /**
     * @dev ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— 변동이 λ°œμƒν–ˆμ„ λ•Œ 이벀트
     * @param backer ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— λ“±μž¬ν•˜λ €λŠ” 지갑 μ£Όμ†Œ
     * @param allowed (true : ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— μΆ”κ°€) (false : 제거)
     */
    event WhiteBacker(address indexed backer, bool allowed);
    
    
    /**
     * @dev ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— λ“±λ‘ν•˜κ±°λ‚˜ ν•΄μ œν•œλ‹€.
     * @param _target ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— λ“±μž¬ν•˜λ €λŠ” 지갑 μ£Όμ†Œ
     * @param _allowed (true : ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— μΆ”κ°€) (false : 제거) 
     */
    function setWhiteBacker(address _target, bool _allowed) onlyOwner public {
        require(_target != 0x0);
        
        if(_allowed == true) {
            list[_target] = 1;
        } else {
            list[_target] = 0;
        }
        
        WhiteBacker(_target, _allowed);
    }
    
    /**
     * @dev ν™”μ΄νŠΈ λ¦¬μŠ€νŠΈμ— 등둝(μΆ”κ°€)ν•œλ‹€
     * @param _target μΆ”κ°€ν•  지갑 μ£Όμ†Œ
     */
    function addWhiteBacker(address _target) onlyOwner public {
        setWhiteBacker(_target, true);
    }
    
    /**
     * @dev ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— μ—¬λŸ¬ 지갑 μ£Όμ†Œλ₯Ό λ™μ‹œμ— λ“±μž¬ν•˜κ±°λ‚˜ μ œκ±°ν•œλ‹€.
     * 
     * κ°€μŠ€ μ†Œλͺ¨λ₯Ό 쀄여보기 μœ„ν•¨
     * @param _backers λŒ€μƒμ΄ λ˜λŠ” μ§€κ°‘λ“€μ˜ 리슀트
     * @param _allows λŒ€μƒμ΄ λ˜λŠ” μ§€κ°‘λ“€μ˜ μΆ”κ°€ μ—¬λΆ€ 리슀트 (true : μΆ”κ°€) (false : 제거)
     */
    function setWhiteBackersByList(address[] _backers, bool[] _allows) onlyOwner public {
        require(_backers.length > 0);
        require(_backers.length == _allows.length);
        
        for(uint backerIndex = 0; backerIndex < _backers.length; backerIndex++) {
            setWhiteBacker(_backers[backerIndex], _allows[backerIndex]);
        }
    }
    
    /**
     * @dev ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— μ—¬λŸ¬ 지갑 μ£Όμ†Œλ₯Ό λ“±μž¬ν•œλ‹€.
     * 
     * λͺ¨λ“  μ£Όμ†Œλ“€μ€ ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— μΆ”κ°€λœλ‹€.
     * @param _backers λŒ€μƒμ΄ λ˜λŠ” μ§€κ°‘λ“€μ˜ 리슀트
     */
    function addWhiteBackersByList(address[] _backers) onlyOwner public {
        for(uint backerIndex = 0; backerIndex < _backers.length; backerIndex++) {
            setWhiteBacker(_backers[backerIndex], true);
        }
    }
    
    
    /**
     * @dev ν•΄λ‹Ή 지갑 μ£Όμ†Œκ°€ ν™”μ΄νŠΈ λ¦¬μŠ€νŠΈμ— λ“±λ‘λ˜μ–΄μžˆλŠ”μ§€ ν™•μΈν•œλ‹€
     * @param _addr λ“±μž¬ μ—¬λΆ€λ₯Ό ν™•μΈν•˜λ €λŠ” μ§€κ°‘μ˜ μ£Όμ†Œ
     * @return (true : λ“±λ‘λ˜μ–΄μžˆμŒ) (false : λ“±λ‘λ˜μ–΄μžˆμ§€ μ•ŠμŒ)
     */
    function isInWhiteList(address _addr) public constant returns (bool) {
        require(_addr != 0x0);
        return list[_addr] > 0;
    }
    
    /**
     * @dev μš”μ²­ν•˜λŠ” 지갑이 ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— λ“±λ‘λ˜μ–΄μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.
     * @return (true : λ“±λ‘λ˜μ–΄μžˆμŒ) (false : λ“±λ‘λ˜μ–΄μžˆμ§€ μ•ŠμŒ)
     */
    function isMeInWhiteList() public constant returns (bool isWhiteBacker) {
        return list[msg.sender] > 0;
    }
}



/**
 * @title APIS Crowd Pre-Sale
 * @dev ν† ν°μ˜ 프리세일을 μˆ˜ν–‰ν•˜κΈ° μœ„ν•œ μ»¨νŠΈλž™νŠΈ
 */
contract ApisCrowdSale is Ownable {
    
    // μ†Œμˆ˜μ  자리수. Eth 18μžλ¦¬μ— λ§žμΆ˜λ‹€
    uint8 public constant decimals = 18;
    
    
    // ν¬λΌμš°λ“œ μ„ΈμΌμ˜ 판맀 λͺ©ν‘œλŸ‰(APIS)
    uint256 public fundingGoal;
    
    // ν˜„μž¬ μ§„ν–‰ν•˜λŠ” 판맀 λͺ©ν‘œλŸ‰ 
    // QTUMκ³Ό κ³΅λ™μœΌλ‘œ νŒλ§€κ°€ μ§„ν–‰λ˜κΈ° λ•Œλ¬Έμ—,  QTUM μͺ½ μ»¨νŠΈλ ‰νŠΈμ™€ ν•©μ‚°ν•œ νŒλ§€λŸ‰μ΄ 총 판맀λͺ©ν‘œλ₯Ό λ„˜μ§€ μ•Šλ„λ‘ ν•˜κΈ° μœ„ν•¨
    uint256 public fundingGoalCurrent;
    
    // 1 ETH으둜 μ‚΄ 수 μžˆλŠ” APIS의 갯수
    uint256 public priceOfApisPerFund;
    

    // λ°œκΈ‰λœ Apis 갯수 (μ˜ˆμ•½ + λ°œν–‰)
    //uint256 public totalSoldApis;
    
    // λ°œν–‰ λŒ€κΈ°μ€‘μΈ APIS 갯수
    //uint256 public totalReservedApis;
    
    // λ°œν–‰λ˜μ„œ 좜금된 APIS 갯수
    //uint256 public totalWithdrawedApis;
    
    
    // μž…κΈˆλœ 투자금의 총앑 (μ˜ˆμ•½ + λ°œν–‰)
    //uint256 public totalReceivedFunds;
    
    // ꡬ맀 ν™•μ • μ „ 투자금의 총앑
    //uint256 public totalReservedFunds;
    
    // ꡬ맀 ν™•μ •λœ 투자금의 총앑
    //uint256 public totalPaidFunds;

    
    // νŒλ§€κ°€ μ‹œμž‘λ˜λŠ” μ‹œκ°„
    uint public startTime;
    
    // νŒλ§€κ°€ μ’…λ£Œλ˜λŠ” μ‹œκ°„
    uint public endTime;

    // νŒλ§€κ°€ 쑰기에 μ’…λ£Œλ  경우λ₯Ό λŒ€λΉ„ν•˜κΈ° μœ„ν•¨
    bool closed = false;
    
	SaleStatus public saleStatus;
    
    // APIS 토큰 μ»¨νŠΈλ ‰νŠΈ
    ApisToken internal tokenReward;
    
    // ν™”μ΄νŠΈλ¦¬μŠ€νŠΈ μ»¨νŠΈλ ‰νŠΈ
    WhiteList internal whiteList;

    
    
    mapping (address => Property) public fundersProperty;
    
    /**
     * @dev APIS 토큰 ꡬ맀자의 μžμ‚° ν˜„ν™©μ„ μ •λ¦¬ν•˜κΈ° μœ„ν•œ ꡬ쑰체
     */
    struct Property {
        uint256 reservedFunds;   // μž…κΈˆν–ˆμ§€λ§Œ 아직 APIS둜 λ³€ν™˜λ˜μ§€ μ•Šμ€ Eth (ν™˜λΆˆ κ°€λŠ₯)
        uint256 paidFunds;    	// APIS둜 λ³€ν™˜λœ Eth (ν™˜λΆˆ λΆˆκ°€)
        uint256 reservedApis;   // 받을 μ˜ˆμ •μΈ 토큰
        uint256 withdrawedApis; // 이미 받은 토큰
        uint purchaseTime;      // κ΅¬μž…ν•œ μ‹œκ°„
    }
	
	
	/**
	 * @dev ν˜„μž¬ μ„ΈμΌμ˜ 진행 ν˜„ν™©μ„ 확인할 수 μžˆλ‹€.
	 * totalSoldApis λ°œκΈ‰λœ Apis 갯수 (μ˜ˆμ•½ + λ°œν–‰)
	 * totalReservedApis λ°œν–‰ λŒ€κΈ° 쀑인 Apis
	 * totalWithdrawedApis λ°œν–‰λ˜μ„œ 좜금된 APIS 갯수
	 * 
	 * totalReceivedFunds μž…κΈˆλœ 투자금의 총앑 (μ˜ˆμ•½ + λ°œν–‰)
	 * totalReservedFunds ꡬ맀 ν™•μ • μ „ 투자금의 총앑
	 * ttotalPaidFunds ꡬ맀 ν™•μ •λœ 투자금의 총앑
	 */
	struct SaleStatus {
		uint256 totalReservedFunds;
		uint256 totalPaidFunds;
		uint256 totalReceivedFunds;
		
		uint256 totalReservedApis;
		uint256 totalWithdrawedApis;
		uint256 totalSoldApis;
	}
    
    
    
    /**
     * @dev APISλ₯Ό κ΅¬μž…ν•˜κΈ° μœ„ν•œ Eth을 μž…κΈˆν–ˆμ„ λ•Œ λ°œμƒν•˜λŠ” 이벀트
     * @param beneficiary APISλ₯Ό κ΅¬λ§€ν•˜κ³ μž ν•˜λŠ” μ§€κ°‘μ˜ μ£Όμ†Œ
     * @param amountOfFunds μž…κΈˆν•œ Eth의 μ–‘ (wei)
     * @param amountOfApis νˆ¬μžκΈˆμ— μƒμ‘ν•˜λŠ” APIS ν† ν°μ˜ μ–‘ (wei)
     */
    event ReservedApis(address beneficiary, uint256 amountOfFunds, uint256 amountOfApis);
    
    /**
     * @dev ν¬λΌμš°λ“œ 세일 μ»¨νŠΈλ ‰νŠΈμ—μ„œ Eth이 μΈμΆœλ˜μ—ˆμ„ λ•Œ λ°œμƒν•˜λŠ” 이벀트
     * @param addr λ°›λŠ” μ§€κ°‘μ˜ μ£Όμ†Œ
     * @param amount μ†‘κΈˆλ˜λŠ” μ–‘(wei)
     */
    event WithdrawalFunds(address addr, uint256 amount);
    
    /**
     * @dev κ΅¬λ§€μžμ—κ²Œ 토큰이 λ°œκΈ‰λ˜μ—ˆμ„ λ•Œ λ°œμƒν•˜λŠ” 이벀트
     * @param funder 토큰을 λ°›λŠ” μ§€κ°‘μ˜ μ£Όμ†Œ
     * @param amountOfFunds μž…κΈˆν•œ 투자금의 μ–‘ (wei)
     * @param amountOfApis λ°œκΈ‰ λ°›λŠ” ν† ν°μ˜ μ–‘ (wei)
     */
    event WithdrawalApis(address funder, uint256 amountOfFunds, uint256 amountOfApis);
    
    
    /**
     * @dev 투자금 μž…κΈˆ ν›„, 아직 토큰을 λ°œκΈ‰λ°›μ§€ μ•Šμ€ μƒνƒœμ—μ„œ, ν™˜λΆˆ 처리λ₯Ό ν–ˆμ„ λ•Œ λ°œμƒν•˜λŠ” 이벀트
     * @param _backer ν™˜λΆˆ 처리λ₯Ό μ§„ν–‰ν•˜λŠ” μ§€κ°‘μ˜ μ£Όμ†Œ
     * @param _amountFunds ν™˜λΆˆν•˜λŠ” 투자금의 μ–‘
     * @param _amountApis μ·¨μ†Œλ˜λŠ” APIS μ–‘
     */
    event Refund(address _backer, uint256 _amountFunds, uint256 _amountApis);
    
    
    /**
     * @dev ν¬λΌμš°λ“œ 세일 진행 μ€‘μ—λ§Œ λ™μž‘ν•˜λ„λ‘ μ œν•œν•˜κ³ , APIS의 가격도 μ„€μ •λ˜μ–΄μ•Όλ§Œ ν•œλ‹€.
     */
    modifier onSale() {
        require(now >= startTime);
        require(now < endTime);
        require(closed == false);
        require(priceOfApisPerFund > 0);
        require(fundingGoalCurrent > 0);
        _;
    }
    
    /**
     * @dev ν¬λΌμš°λ“œ 세일 μ’…λ£Œ ν›„μ—λ§Œ λ™μž‘ν•˜λ„λ‘ μ œν•œ
     */
    modifier onFinished() {
        require(now >= endTime || closed == true);
        _;
    }
    
    /**
     * @dev ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— λ“±λ‘λ˜μ–΄μžˆμ–΄μ•Όν•˜κ³  아직 κ΅¬λ§€μ™„λ£Œ λ˜μ§€ μ•Šμ€ 투자금이 μžˆμ–΄μ•Όλ§Œ ν•œλ‹€.
     */
    modifier claimable() {
        require(whiteList.isInWhiteList(msg.sender) == true);
        require(fundersProperty[msg.sender].reservedFunds > 0);
        _;
    }
    
    
    /**
     * @dev ν¬λΌμš°λ“œ 세일 μ»¨νŠΈλ ‰νŠΈλ₯Ό μƒμ„±ν•œλ‹€.
     * @param _fundingGoalApis νŒλ§€ν•˜λŠ” ν† ν°μ˜ μ–‘ (APIS λ‹¨μœ„)
     * @param _startTime ν¬λΌμš°λ“œ 세일을 μ‹œμž‘ν•˜λŠ” μ‹œκ°„
     * @param _endTime ν¬λΌμš°λ“œ 세일을 μ’…λ£Œν•˜λŠ” μ‹œκ°„
     * @param _addressOfApisTokenUsedAsReward APIS ν† ν°μ˜ μ»¨νŠΈλ ‰νŠΈ μ£Όμ†Œ
     * @param _addressOfWhiteList WhiteList μ»¨νŠΈλ ‰νŠΈ μ£Όμ†Œ
     */
    function ApisCrowdSale (
        uint256 _fundingGoalApis,
        uint _startTime,
        uint _endTime,
        address _addressOfApisTokenUsedAsReward,
        address _addressOfWhiteList
    ) public {
        require (_fundingGoalApis > 0);
        require (_startTime > now);
        require (_endTime > _startTime);
        require (_addressOfApisTokenUsedAsReward != 0x0);
        require (_addressOfWhiteList != 0x0);
        
        fundingGoal = _fundingGoalApis * 10 ** uint256(decimals);
        
        startTime = _startTime;
        endTime = _endTime;
        
        // 토큰 μŠ€λ§ˆνŠΈμ»¨νŠΈλ ‰νŠΈλ₯Ό λΆˆλŸ¬μ˜¨λ‹€
        tokenReward = ApisToken(_addressOfApisTokenUsedAsReward);
        
        // ν™”μ΄νŠΈ 리슀트λ₯Ό κ°€μ Έμ˜¨λ‹€
        whiteList = WhiteList(_addressOfWhiteList);
    }
    
    /**
     * @dev 판맀 μ’…λ£ŒλŠ” 1회만 κ°€λŠ₯ν•˜λ„λ‘ μ œμ•½ν•œλ‹€. μ’…λ£Œ ν›„ λ‹€μ‹œ 판맀 μ€‘μœΌλ‘œ λ³€κ²½ν•  수 μ—†λ‹€
     */
    function closeSale(bool _closed) onlyOwner public {
        require (closed == false);
        
        closed = _closed;
    }
    
    /**
     * @dev ν¬λΌμš°λ“œ 세일 μ‹œμž‘ 전에 1Eth에 ν•΄λ‹Ήν•˜λŠ” APIS λŸ‰μ„ μ„€μ •ν•œλ‹€.
     */
    function setPriceOfApis(uint256 price) onlyOwner public {
        require(priceOfApisPerFund == 0);
        
        priceOfApisPerFund = price;
    }
    
    /**
     * @dev ν˜„ μ‹œμ μ—μ„œ 판맀 κ°€λŠ₯ν•œ λͺ©ν‘œλŸ‰μ„ μˆ˜μ •ν•œλ‹€.
     * @param _currentFundingGoalAPIS ν˜„ μ‹œμ μ˜ 판맀 λͺ©ν‘œλŸ‰μ€ 총 판맀된 μ–‘ μ΄μƒμ΄μ–΄μ•Όλ§Œ ν•œλ‹€.
     */
    function setCurrentFundingGoal(uint256 _currentFundingGoalAPIS) onlyOwner public {
        uint256 fundingGoalCurrentWei = _currentFundingGoalAPIS * 10 ** uint256(decimals);
        require(fundingGoalCurrentWei >= saleStatus.totalSoldApis);
        
        fundingGoalCurrent = fundingGoalCurrentWei;
    }
    
    
    /**
     * @dev APIS μž”κ³ λ₯Ό ν™•μΈν•œλ‹€
     * @param _addr μž”κ³ λ₯Ό ν™•μΈν•˜λ €λŠ” μ§€κ°‘μ˜ μ£Όμ†Œ
     * @return balance 지갑에 듀은 APIS μž”κ³  (wei)
     */
    function balanceOf(address _addr) public view returns (uint256 balance) {
        return tokenReward.balanceOf(_addr);
    }
    
    /**
     * @dev ν™”μ΄νŠΈλ¦¬μŠ€νŠΈ 등둝 μ—¬λΆ€λ₯Ό ν™•μΈν•œλ‹€
     * @param _addr 등둝 μ—¬λΆ€λ₯Ό ν™•μΈν•˜λ €λŠ” μ£Όμ†Œ
     * @return addrIsInWhiteList true : λ“±λ‘λ˜μžˆμŒ, false : λ“±λ‘λ˜μ–΄μžˆμ§€ μ•ŠμŒ
     */
    function whiteListOf(address _addr) public view returns (string message) {
        if(whiteList.isInWhiteList(_addr) == true) {
            return "The address is in whitelist.";
        } else {
            return "The address is *NOT* in whitelist.";
        }
    }
    
    
    /**
     * @dev 전달받은 지갑이 APIS 지급 μš”μ²­μ΄ κ°€λŠ₯ν•œμ§€ ν™•μΈν•œλ‹€.
     * @param _addr ν™•μΈν•˜λŠ” μ£Όμ†Œ
     * @return message κ²°κ³Ό λ©”μ‹œμ§€
     */
    function isClaimable(address _addr) public view returns (string message) {
        if(fundersProperty[_addr].reservedFunds == 0) {
            return "The address has no claimable balance.";
        }
        
        if(whiteList.isInWhiteList(_addr) == false) {
            return "The address must be registered with KYC and Whitelist";
        }
        
        else {
            return "The address can claim APIS!";
        }
    }
    
    
    /**
     * @dev ν¬λΌμš°λ“œ 세일 μ»¨νŠΈλ ‰νŠΈλ‘œ λ°”λ‘œ νˆ¬μžκΈˆμ„ μ†‘κΈˆν•˜λŠ” 경우, buyToken으둜 μ—°κ²°ν•œλ‹€
     */
    function () onSale public payable {
        buyToken(msg.sender);
    }
    
    /**
     * @dev 토큰을 κ΅¬μž…ν•˜κΈ° μœ„ν•΄ Qtum을 μž…κΈˆλ°›λŠ”λ‹€.
     * @param _beneficiary 토큰을 λ°›κ²Œ 될 μ§€κ°‘μ˜ μ£Όμ†Œ
     */
    function buyToken(address _beneficiary) onSale public payable {
        // μ£Όμ†Œ 확인
        require(_beneficiary != 0x0);
        
        // ν¬λΌμš°λ“œ 세일 μ»¨νŠΈλ ‰νŠΈμ˜ 토큰 μ†‘κΈˆ κΈ°λŠ₯이 μ •μ§€λ˜μ–΄μžˆμœΌλ©΄ νŒλ§€ν•˜μ§€ μ•ŠλŠ”λ‹€
        bool isLocked = false;
        uint timeLock = 0;
        (isLocked, timeLock) = tokenReward.isWalletLocked_Send(this);
        
        require(isLocked == false);
        
        
        uint256 amountFunds = msg.value;
        uint256 reservedApis = amountFunds * priceOfApisPerFund;
        
        
        // λͺ©ν‘œ κΈˆμ•‘μ„ λ„˜μ–΄μ„œμ§€ λͺ»ν•˜λ„둝 ν•œλ‹€
        require(saleStatus.totalSoldApis + reservedApis <= fundingGoalCurrent);
        require(saleStatus.totalSoldApis + reservedApis <= fundingGoal);
        
        // 투자자의 μžμ‚°μ„ μ—…λ°μ΄νŠΈν•œλ‹€
        fundersProperty[_beneficiary].reservedFunds += amountFunds;
        fundersProperty[_beneficiary].reservedApis += reservedApis;
        fundersProperty[_beneficiary].purchaseTime = now;
        
        // 총앑듀을 μ—…λ°μ΄νŠΈν•œλ‹€
        saleStatus.totalReceivedFunds += amountFunds;
        saleStatus.totalReservedFunds += amountFunds;
        
        saleStatus.totalSoldApis += reservedApis;
        saleStatus.totalReservedApis += reservedApis;
        
        
        // ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— λ“±λ‘λ˜μ–΄μžˆμœΌλ©΄ λ°”λ‘œ μΆœκΈˆν•œλ‹€
        if(whiteList.isInWhiteList(_beneficiary) == true) {
            withdrawal(_beneficiary);
        }
        else {
            // 토큰 λ°œν–‰ μ˜ˆμ•½ 이벀트 λ°œμƒ
            ReservedApis(_beneficiary, amountFunds, reservedApis);
        }
    }
    
    
    
    /**
     * @dev κ΄€λ¦¬μžμ— μ˜ν•΄μ„œ 토큰을 λ°œκΈ‰ν•œλ‹€. ν•˜μ§€λ§Œ κΈ°λ³Έ μš”κ±΄μ€ κ°–μΆ°μ•Όλ§Œ κ°€λŠ₯ν•˜λ‹€
     * 
     * @param _target 토큰 λ°œκΈ‰μ„ μ²­κ΅¬ν•˜λ €λŠ” 지갑 μ£Όμ†Œ
     */
    function claimApis(address _target) public {
        // ν™”μ΄νŠΈ λ¦¬μŠ€νŠΈμ— μžˆμ–΄μ•Όλ§Œ ν•˜κ³ 
        require(whiteList.isInWhiteList(_target) == true);
        // μ˜ˆμ•½λœ 투자금이 μžˆμ–΄μ•Όλ§Œ ν•œλ‹€.
        require(fundersProperty[_target].reservedFunds > 0);
        
        withdrawal(_target);
    }
    
    /**
     * @dev μ˜ˆμ•½ν•œ ν† ν°μ˜ μ‹€μ œ 지급을 μš”μ²­ν•˜λ„λ‘ ν•œλ‹€.
     * 
     * APISλ₯Ό κ΅¬λ§€ν•˜κΈ° μœ„ν•΄ Qtum을 μž…κΈˆν•  경우, κ΄€λ¦¬μžμ˜ κ²€ν† λ₯Ό μœ„ν•œ 7일의 μœ μ˜ˆκΈ°κ°„μ΄ μ‘΄μž¬ν•œλ‹€.
     * μœ μ˜ˆκΈ°κ°„μ΄ μ§€λ‚˜λ©΄ 토큰 지급을 μš”κ΅¬ν•  수 μžˆλ‹€.
     */
    function claimMyApis() claimable public {
        withdrawal(msg.sender);
    }
    
    
    /**
     * @dev κ΅¬λ§€μžμ—κ²Œ 토큰을 μ§€κΈ‰ν•œλ‹€.
     * @param funder 토큰을 지급할 μ§€κ°‘μ˜ μ£Όμ†Œ
     */
    function withdrawal(address funder) internal {
        // ꡬ맀자 μ§€κ°‘μœΌλ‘œ 토큰을 μ „λ‹¬ν•œλ‹€
        assert(tokenReward.transferFrom(owner, funder, fundersProperty[funder].reservedApis));
        
        fundersProperty[funder].withdrawedApis += fundersProperty[funder].reservedApis;
        fundersProperty[funder].paidFunds += fundersProperty[funder].reservedFunds;
        
        // 총앑에 반영
        saleStatus.totalReservedFunds -= fundersProperty[funder].reservedFunds;
        saleStatus.totalPaidFunds += fundersProperty[funder].reservedFunds;
        
        saleStatus.totalReservedApis -= fundersProperty[funder].reservedApis;
        saleStatus.totalWithdrawedApis += fundersProperty[funder].reservedApis;
        
        // APISκ°€ 좜금 λ˜μ—ˆμŒμ„ μ•Œλ¦¬λŠ” 이벀트
        WithdrawalApis(funder, fundersProperty[funder].reservedFunds, fundersProperty[funder].reservedApis);
        
        // μΈμΆœν•˜μ§€ μ•Šμ€ APIS μž”κ³ λ₯Ό 0으둜 λ³€κ²½ν•΄μ„œ, Qtum μž¬μž…κΈˆ μ‹œ 이미 μΆœκΈˆν•œ 토큰이 λ‹€μ‹œ μΆœκΈˆλ˜μ§€ μ•Šκ²Œ ν•œλ‹€.
        fundersProperty[funder].reservedFunds = 0;
        fundersProperty[funder].reservedApis = 0;
    }
    
    
    /**
     * @dev 아직 토큰을 λ°œκΈ‰λ°›μ§€ μ•Šμ€ 지갑을 λŒ€μƒμœΌλ‘œ, ν™˜λΆˆμ„ 진행할 수 μžˆλ‹€.
     * @param _funder ν™˜λΆˆμ„ μ§„ν–‰ν•˜λ €λŠ” μ§€κ°‘μ˜ μ£Όμ†Œ
     */
    function refundByOwner(address _funder) onlyOwner public {
        require(fundersProperty[_funder].reservedFunds > 0);
        
        uint256 amountFunds = fundersProperty[_funder].reservedFunds;
        uint256 amountApis = fundersProperty[_funder].reservedApis;
        
        // Eth을 ν™˜λΆˆν•œλ‹€
        _funder.transfer(amountFunds);
        
        saleStatus.totalReceivedFunds -= amountFunds;
        saleStatus.totalReservedFunds -= amountFunds;
        
        saleStatus.totalSoldApis -= amountApis;
        saleStatus.totalReservedApis -= amountApis;
        
        fundersProperty[_funder].reservedFunds = 0;
        fundersProperty[_funder].reservedApis = 0;
        
        Refund(_funder, amountFunds, amountApis);
    }
    
    
    /**
     * @dev νŽ€λ”©μ΄ μ’…λ£Œλœ 이후면, 적립된 νˆ¬μžκΈˆμ„ λ°˜ν™˜ν•œλ‹€.
     * @param remainRefundable true : ν™˜λΆˆν•  수 μžˆλŠ” κΈˆμ•‘μ€ 남기고 λ°˜ν™˜ν•œλ‹€. false : λͺ¨λ‘ λ°˜ν™˜ν•œλ‹€
     */
    function withdrawalFunds(bool remainRefundable) onlyOwner public {
        require(now > endTime || closed == true);
        
        uint256 amount = 0;
        if(remainRefundable) {
            amount = this.balance - saleStatus.totalReservedFunds;
        } else {
            amount = this.balance;
        }
        
        if(amount > 0) {
            msg.sender.transfer(amount);
            
            WithdrawalFunds(msg.sender, amount);
        }
    }
    
    /**
	 * @dev ν¬λΌμš°λ“œ 세일이 진행 쀑인지 μ—¬λΆ€λ₯Ό λ°˜ν™˜ν•œλ‹€.
	 * @return isOpened true: 진행 쀑 false : 진행 쀑이 μ•„λ‹˜(μ°Έμ—¬ λΆˆκ°€)
	 */
    function isOpened() public view returns (bool isOpend) {
        if(now < startTime) return false;
        if(now >= endTime) return false;
        if(closed == true) return false;
        
        return true;
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):