编译器
0.8.20+commit.a1b79de6
文件 1 的 5:AggregatorV3Interface.sol
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
文件 2 的 5:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 3 的 5:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 4 的 5:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 5 的 5:TokenSale.sol
pragma solidity 0.8.20;
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract TokenSale is Ownable {
uint256 public constant SALE_DURATION = 1_900_510;
uint256 public constant LOCK_DURATION = 15_768_000;
uint256 public constant VESTING_DURATION = 25_920_000;
uint256 public constant MAX_SUPPLY = 30_000_000 * 10 ** 18;
string public constant VIRTUAL_BONDING_CURVE_COEFFICIENT = "0.000000005";
AggregatorV3Interface public immutable priceFeed;
bool public isPaused;
bool public saleOpen;
bool public hasReclaimed;
uint256 public totalPurchased;
uint256 public startingBalance;
uint256 public saleEndTime;
uint256 public lockEndTime;
uint256 public vestingEndTime;
uint256 public whitelistCount;
uint256 public presaleMinimum = 5000 * 10 ** 6;
uint256 public whitelistMinimum = 1000 * 10 ** 6;
uint256 public tokensPerUSD = (25 * 10 ** 18) / 10 ** 6;
address public adminAddress;
IERC20 public yoloToken;
mapping(address => uint256) public purchasedAmount;
mapping(address => uint256) public claimedAmount;
mapping(address => bool) public whitelist;
event SaleOpen(
uint256 timestamp,
uint256 saleEndTime,
uint256 lockEndTime,
uint256 vestingEndTime
);
event Buy(address indexed user, uint256 weiIn, uint256 tokenAllocated);
event TokensClaimed(address indexed user, uint256 tokensToClaim);
event Withdraw(address indexed receiver, uint256 weiAmount);
event TokenReclamation(address indexed receiver, uint256 weiAmount);
modifier onlyAdmin() {
require(
msg.sender == adminAddress || msg.sender == owner(),
"Only admin may call"
);
_;
}
modifier whenSaleOpen() {
require(saleOpen, "Sale is not open");
require(block.timestamp < saleEndTime, "Sale has ended");
_;
}
modifier beforeSaleOpen() {
require(!saleOpen, "Sale is already open");
_;
}
modifier whenUnpaused() {
require(!isPaused, "Sale is paused");
_;
}
modifier whenClaimingPeriod() {
require(block.timestamp >= lockEndTime, "Tokens are locked");
_;
}
modifier canReclaim() {
require(!hasReclaimed, "Tokens already reclaimed");
_;
}
constructor(address _token) {
yoloToken = IERC20(_token);
priceFeed = AggregatorV3Interface(
0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
);
}
function getVestedAmount(address user) public view returns (uint256) {
if (block.timestamp < lockEndTime || lockEndTime == 0) {
return 0;
} else if (block.timestamp >= vestingEndTime) {
return purchasedAmount[user];
} else {
uint256 vestingDuration = vestingEndTime - lockEndTime;
uint256 timeSinceLockEnd = block.timestamp - lockEndTime;
return (purchasedAmount[user] * timeSinceLockEnd) / vestingDuration;
}
}
function setAdminAddress(address _adminAddress) external onlyOwner {
require(_adminAddress != address(0), "Admin must not be zero address");
adminAddress = _adminAddress;
}
function pause() external onlyOwner {
require(!isPaused, "Already paused");
isPaused = true;
}
function unpause() external onlyOwner {
require(isPaused, "Already unpaused");
isPaused = false;
}
function addToWhitelist(address user) external onlyAdmin {
require(whitelistCount < 100, "100 max reached");
require(!whitelist[user], "User already whitelisted");
whitelist[user] = true;
++whitelistCount;
}
function setWhitelistMinimum(uint256 minimum) external onlyOwner {
whitelistMinimum = minimum;
}
function setPresaleMinimum(uint256 minimum) external onlyOwner {
presaleMinimum = minimum;
}
function getLatestEthPrice() public view returns (uint256) {
(, int256 price, , , ) = priceFeed.latestRoundData();
return uint256(price);
}
function openSale() external onlyOwner beforeSaleOpen {
require(
yoloToken.balanceOf(address(this)) >= MAX_SUPPLY,
"Not enough tokens transferred to contract for sale"
);
saleOpen = true;
saleEndTime = block.timestamp + SALE_DURATION;
lockEndTime = saleEndTime + LOCK_DURATION;
vestingEndTime = lockEndTime + VESTING_DURATION;
startingBalance = yoloToken.balanceOf(address(this));
}
function buy() external payable whenSaleOpen whenUnpaused {
uint256 latestEthPrice = getLatestEthPrice();
uint256 usdAmount = (latestEthPrice * msg.value) / 10 ** 20;
require(usdAmount >= presaleMinimum, "Below amount minimum");
_buy(usdAmount);
}
function whitelistBuy() external payable whenSaleOpen whenUnpaused {
uint256 latestEthPrice = getLatestEthPrice();
uint256 usdAmount = (latestEthPrice * msg.value) / 10 ** 20;
require(whitelist[msg.sender] == true, "Not in whitelist");
require(
usdAmount >= whitelistMinimum,
"Below whitelist amount minimum"
);
_buy(usdAmount);
}
function _buy(uint256 usdAmount) internal {
uint256 tokenAmount = usdAmount * tokensPerUSD;
require(
totalPurchased + tokenAmount <= MAX_SUPPLY,
"Max supply exceeded"
);
totalPurchased += tokenAmount;
purchasedAmount[msg.sender] += tokenAmount;
emit Buy(msg.sender, msg.value, tokenAmount);
}
function claim() external whenClaimingPeriod {
uint256 tokensToClaim = getVestedAmount(msg.sender) -
claimedAmount[msg.sender];
require(tokensToClaim > 0, "No tokens to claim");
yoloToken.transfer(msg.sender, tokensToClaim);
claimedAmount[msg.sender] += tokensToClaim;
emit TokensClaimed(msg.sender, tokensToClaim);
}
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "No ether to withdraw");
payable(owner()).transfer(balance);
emit Withdraw(msg.sender, balance);
}
function reclaimTokens() external onlyOwner canReclaim {
require(block.timestamp > saleEndTime, "Reclaiming is not allowed yet");
uint256 remainingBalance = startingBalance - totalPurchased;
require(remainingBalance > 0, "No tokens to reclaim");
yoloToken.transfer(msg.sender, remainingBalance);
hasReclaimed = true;
emit TokenReclamation(msg.sender, remainingBalance);
}
}
{
"compilationTarget": {
"contracts/TokenSale.sol": "TokenSale"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 350
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"weiIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAllocated","type":"uint256"}],"name":"Buy","type":"event"},{"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":"timestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"saleEndTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockEndTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vestingEndTime","type":"uint256"}],"name":"SaleOpen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"weiAmount","type":"uint256"}],"name":"TokenReclamation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensToClaim","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"weiAmount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"LOCK_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SALE_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VESTING_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VIRTUAL_BONDING_CURVE_COEFFICIENT","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"addToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adminAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestEthPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getVestedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasReclaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"presaleMinimum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeed","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"purchasedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reclaimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"saleEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"saleOpen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_adminAddress","type":"address"}],"name":"setAdminAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minimum","type":"uint256"}],"name":"setPresaleMinimum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minimum","type":"uint256"}],"name":"setWhitelistMinimum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startingBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensPerUSD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPurchased","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":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vestingEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistBuy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"whitelistCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistMinimum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"yoloToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}]