// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/SafeERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";
contract ITOPool is Ownable, ReentrancyGuard {
using SafeMath for uint256;
using SafeERC20 for ERC20;
uint256 public tokenPrice;
ERC20 public rewardToken;
uint256 public decimals;
uint256 public startTimestamp;
uint256 public finishTimestamp;
uint256 public startClaimTimestamp;
uint256 public minEthPayment;
uint256 public maxEthPayment;
uint256 public maxDistributedTokenAmount;
uint256 public tokensForDistribution;
uint256 public distributedTokens;
struct UserInfo {
uint debt;
uint total;
uint totalInvestedETH;
}
mapping(address => UserInfo) public userInfo;
event TokensDebt(
address indexed holder,
uint256 ethAmount,
uint256 tokenAmount
);
event TokensWithdrawn(address indexed holder, uint256 amount);
constructor(
uint256 _tokenPrice,
ERC20 _rewardToken,
uint256 _startTimestamp,
uint256 _finishTimestamp,
uint256 _startClaimTimestamp,
uint256 _minEthPayment,
uint256 _maxEthPayment,
uint256 _maxDistributedTokenAmount
) public {
tokenPrice = _tokenPrice;
rewardToken = _rewardToken;
decimals = rewardToken.decimals();
require(
_startTimestamp < _finishTimestamp,
"Start timestamp must be less than finish timestamp"
);
require(
_finishTimestamp > now,
"Finish timestamp must be more than current block"
);
startTimestamp = _startTimestamp;
finishTimestamp = _finishTimestamp;
startClaimTimestamp = _startClaimTimestamp;
minEthPayment = _minEthPayment;
maxEthPayment = _maxEthPayment;
maxDistributedTokenAmount = _maxDistributedTokenAmount;
}
function pay() payable external {
require(msg.value >= minEthPayment, "Less then min amount");
require(msg.value <= maxEthPayment, "More then max amount");
require(now >= startTimestamp, "Not started");
require(now < finishTimestamp, "Ended");
uint256 tokenAmount = getTokenAmount(msg.value);
require(tokensForDistribution.add(tokenAmount) <= maxDistributedTokenAmount, "Overfilled");
UserInfo storage user = userInfo[msg.sender];
require(user.totalInvestedETH.add(msg.value) <= maxEthPayment, "More then max amount");
tokensForDistribution = tokensForDistribution.add(tokenAmount);
user.totalInvestedETH = user.totalInvestedETH.add(msg.value);
user.total = user.total.add(tokenAmount);
user.debt = user.debt.add(tokenAmount);
emit TokensDebt(msg.sender, msg.value, tokenAmount);
}
function getTokenAmount(uint256 ethAmount)
internal
view
returns (uint256)
{
return ethAmount.div(tokenPrice).mul(10**decimals);
}
/// @dev Allows to claim tokens for the specific user.
/// @param _user Token receiver.
function claimFor(address _user) external {
proccessClaim(_user);
}
/// @dev Allows to claim tokens for themselves.
function claim() external {
proccessClaim(msg.sender);
}
/// @dev Proccess the claim.
/// @param _receiver Token receiver.
function proccessClaim(
address _receiver
) internal nonReentrant{
require(now > startClaimTimestamp, "Distribution not started");
UserInfo storage user = userInfo[_receiver];
uint256 _amount = user.debt;
if (_amount > 0) {
user.debt = 0;
distributedTokens = distributedTokens.add(_amount);
rewardToken.safeTransfer(_receiver, _amount);
emit TokensWithdrawn(_receiver,_amount);
}
}
function withdrawETH(uint256 amount) external onlyOwner{
// This forwards all available gas. Be sure to check the return value!
(bool success, ) = msg.sender.call.value(amount)("");
require(success, "Transfer failed.");
}
function withdrawNotSoldTokens() external onlyOwner returns(bool success) {
require(now > finishTimestamp, "Withdraw allowed after stop accept ETH");
uint256 balance = rewardToken.balanceOf(address(this));
rewardToken.safeTransfer(msg.sender, balance.add(distributedTokens).sub(tokensForDistribution));
return true;
}
}
{
"compilationTarget": {
"browser/ITO.sol": "ITOPool"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"uint256","name":"_tokenPrice","type":"uint256"},{"internalType":"contract ERC20","name":"_rewardToken","type":"address"},{"internalType":"uint256","name":"_startTimestamp","type":"uint256"},{"internalType":"uint256","name":"_finishTimestamp","type":"uint256"},{"internalType":"uint256","name":"_startClaimTimestamp","type":"uint256"},{"internalType":"uint256","name":"_minEthPayment","type":"uint256"},{"internalType":"uint256","name":"_maxEthPayment","type":"uint256"},{"internalType":"uint256","name":"_maxDistributedTokenAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"TokensDebt","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensWithdrawn","type":"event"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"claimFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"distributedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finishTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxDistributedTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxEthPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minEthPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pay","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startClaimTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensForDistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"totalInvestedETH","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawNotSoldTokens","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]