// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC20 {
function transfer(address recipient, uint256 amount)
external
returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract TokenPresale {
IERC20 public token;
address public owner;
address public pendingOwner;
uint256 public price;
bool public saleActive;
bool private locked;
event Purchase(address indexed buyer, uint256 amount);
event PriceUpdated(uint256 newPrice);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
constructor(address _token, uint256 _price) {
require(_price > 0, "Price should be greater than 0");
token = IERC20(_token);
owner = msg.sender;
price = _price;
saleActive = false;
locked = false;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
modifier onlyPendingOwner() {
require(
msg.sender == pendingOwner,
"Only pending owner can call this function"
);
_;
}
modifier noReentrancy() {
require(!locked, "No reentrancy allowed");
locked = true;
_;
locked = false;
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "New owner is the zero address");
pendingOwner = newOwner;
}
function acceptOwnership() external onlyPendingOwner {
emit OwnershipTransferred(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
function startSale() external onlyOwner {
require(!saleActive, "Sale already active");
saleActive = true;
}
function stopSale() external onlyOwner {
require(saleActive, "Sale not active");
saleActive = false;
}
function buyTokens(uint256 tokenAmount) external payable noReentrancy {
require(saleActive, "Sale not active");
require(tokenAmount > 0, "Cannot purchase 0 tokens");
uint256 tokenToTransfer = tokenAmount * 10**18;
require(
token.balanceOf(address(this)) >= tokenToTransfer,
"Insufficient tokens"
);
uint256 requiredETH = tokenAmount * price;
require(msg.value >= requiredETH, "Insufficient ETH sent");
token.transfer(msg.sender, tokenToTransfer);
emit Purchase(msg.sender, tokenAmount);
if (msg.value > requiredETH) {
payable(msg.sender).transfer(msg.value - requiredETH);
}
}
function setPrice(uint256 _price) external onlyOwner {
require(_price > 0, "Price should be greater than 0");
price = _price;
emit PriceUpdated(_price);
}
function withdrawETH() external onlyOwner {
require(!saleActive, "Sale must be stopped before withdrawing ETH");
payable(owner).transfer(address(this).balance);
}
function withdrawTokens() external onlyOwner {
require(!saleActive, "Sale must be stopped before withdrawing tokens");
token.transfer(owner, token.balanceOf(address(this)));
}
}
{
"compilationTarget": {
"TokenPresale.sol": "TokenPresale"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_price","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":false,"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"PriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Purchase","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"buyTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"saleActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"setPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stopSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}]