编译器
0.8.19+commit.7dd6d404
文件 1 的 16:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 16:BaseClaim.sol
pragma solidity 0.8.19;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract BaseClaim is Ownable {
using SafeERC20 for ERC20;
struct UserInfo {
uint256 reward;
uint256 withdrawn;
}
mapping(address => UserInfo) public userInfo;
uint256 public claimTime;
ERC20 public immutable rewardToken;
event RewardClaimed(
address indexed user,
uint256 indexed withdrawAmount,
uint256 totalWithdrawn
);
event ClaimsPaused();
event ClaimsUnpaused();
uint256 public totalRewards;
uint256 public totalWithdrawn;
bool public areClaimsPaused;
constructor(address _rewardToken) {
require(
address(_rewardToken) != address(0),
"Reward token must be set"
);
rewardToken = ERC20(_rewardToken);
claimTime = block.timestamp;
}
modifier onlyWithRewards(address addr) {
require(userInfo[addr].reward > 0, "Address has no rewards");
_;
}
function pauseClaims() external onlyOwner {
areClaimsPaused = true;
emit ClaimsPaused();
}
function unPauseClaims() external onlyOwner {
areClaimsPaused = false;
emit ClaimsUnpaused();
}
function addUserReward(address _user, uint256 _amount) internal {
UserInfo storage user = userInfo[_user];
uint256 newReward = user.reward + _amount;
totalRewards = totalRewards + _amount;
user.reward = newReward;
}
function setUserReward(address _user, uint256 _amount) internal {
UserInfo storage user = userInfo[_user];
totalRewards = (totalRewards + _amount) - (user.reward);
user.reward = _amount;
require(user.reward >= user.withdrawn, "Invalid reward amount");
}
function freezeUserReward(address _user) internal {
UserInfo storage user = userInfo[_user];
uint256 change = user.reward - user.withdrawn;
user.reward = user.withdrawn;
totalRewards = totalRewards - change;
}
function claim() external onlyWithRewards(msg.sender) {
require(!areClaimsPaused, "Claims are paused");
UserInfo storage user = userInfo[msg.sender];
uint256 withdrawAmount = getWithdrawableAmount(msg.sender);
user.withdrawn = user.withdrawn + withdrawAmount;
totalWithdrawn = totalWithdrawn + withdrawAmount;
assert(user.withdrawn <= user.reward);
rewardToken.safeTransfer(msg.sender, withdrawAmount);
emit RewardClaimed(msg.sender, withdrawAmount, user.withdrawn);
}
function getWithdrawableAmount(
address _user
) public view returns (uint256) {
UserInfo memory user = userInfo[_user];
uint256 unlockedAmount = calculateUnlockedAmount(
user.reward,
block.timestamp
);
return unlockedAmount - user.withdrawn;
}
function calculateUnlockedAmount(
uint256 _totalAmount,
uint256 _timestamp
) internal view virtual returns (uint256) {
return _timestamp > claimTime ? _totalAmount : 0;
}
function totalAvailableAfter() public view virtual returns (uint256) {
return claimTime;
}
function withdrawRewardAmount() external onlyOwner {
rewardToken.safeTransfer(
msg.sender,
rewardToken.balanceOf(address(this)) - totalRewards
);
}
function emergencyWithdrawToken(ERC20 tokenAddress) external onlyOwner {
tokenAddress.safeTransfer(
msg.sender,
tokenAddress.balanceOf(address(this))
);
}
}
文件 3 的 16:Claim.sol
pragma solidity 0.8.19;
import "./VestedClaim.sol";
contract Claim is VestedClaim {
event ClaimantsAdded(
address[] indexed claimants,
uint256[] indexed amounts
);
event RewardsFrozen(address[] indexed claimants);
constructor(uint256 _claimTime, address _token) VestedClaim(_token) {
claimTime = _claimTime;
}
function updateClaimTimestamp(uint256 _claimTime) external onlyOwner {
claimTime = _claimTime;
}
function addClaimants(
address[] calldata _claimants,
uint256[] calldata _claimAmounts
) external onlyOwner {
require(
_claimants.length == _claimAmounts.length,
"Arrays do not have equal length"
);
for (uint256 i = 0; i < _claimants.length; i++) {
setUserReward(_claimants[i], _claimAmounts[i]);
}
emit ClaimantsAdded(_claimants, _claimAmounts);
}
function freezeRewards(address[] memory _claimants) external onlyOwner {
for (uint256 i = 0; i < _claimants.length; i++) {
freezeUserReward(_claimants[i]);
}
emit RewardsFrozen(_claimants);
}
function setUserInfo(
address[] calldata _users,
uint256[] calldata _rewards,
uint256[] calldata _withdrawns
) external onlyOwner {
require(
_users.length == _rewards.length &&
_users.length == _withdrawns.length,
"Arrays do not have equal length"
);
for (uint256 i = 0; i < _users.length; i++) {
_setUserInfo(_users[i], _rewards[i], _withdrawns[i]);
}
emit ClaimantsAdded(_users, _rewards);
}
function _setUserInfo(
address _user,
uint256 _reward,
uint256 _withdrawn
) internal {
UserInfo storage user = userInfo[_user];
user.reward = _reward;
user.withdrawn = _withdrawn;
totalRewards += _reward;
totalWithdrawn += _withdrawn;
require(user.reward >= user.withdrawn, "Invalid reward amount");
}
}
文件 4 的 16:ClaimConfigurable.sol
pragma solidity 0.8.19;
import "./Claim.sol";
contract ClaimConfigurable is Claim {
constructor(
uint256 _claimTime,
address _token,
uint256[4] memory vestingData
) Claim(_claimTime, _token) {
require(vestingData[0] <= BASE_POINTS, "initialUnlock too high");
initialUnlock = vestingData[0];
cliff = vestingData[1];
vesting = vestingData[2];
vestingInterval = vestingData[3];
}
}
文件 5 的 16: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;
}
}
文件 6 的 16:ERC20.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
文件 7 的 16: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);
}
文件 8 的 16:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 9 的 16:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 10 的 16:MoonSaleBase.sol
pragma solidity 0.8.19;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../claim/ClaimConfigurable.sol";
abstract contract MoonSaleBase is ClaimConfigurable, Pausable, ReentrancyGuard {
using SafeERC20 for IERC20;
uint256 public startTime;
uint256 public endTime;
uint256 public maxTotal;
uint256 public total;
address public treasury;
struct PaymentToken {
IERC20 token;
uint256 price;
}
PaymentToken[] public paymentTokens;
event PaymentTokenUpdated(address token, uint256 price);
event PaymentTokensReset();
event Bought(
address indexed by,
address indexed paymentToken,
uint256 paymentAmount,
uint256 buyAmount
);
constructor(
address _saleToken,
uint256 _price,
address _defaultPaymentToken,
uint256 _startTime,
uint256 _endTime,
uint256 _claimTime,
uint256 _maxTotal,
address _treasury,
uint256[4] memory vestingData
) ClaimConfigurable(_claimTime, _saleToken, vestingData) {
paymentTokens.push(PaymentToken(IERC20(_defaultPaymentToken), _price));
startTime = _startTime;
endTime = _endTime;
maxTotal = _maxTotal;
treasury = _treasury;
claimTime = _claimTime;
}
modifier withAllocation(address _address) virtual {
require(
getAvailableAllocation(_address) > 0,
"No available allocation"
);
_;
}
function setStartTime(uint256 _startTime) external onlyOwner {
startTime = _startTime;
}
function setEndTime(uint256 _endTime) external onlyOwner {
endTime = _endTime;
}
function setMaxTotal(uint256 _maxTotal) external onlyOwner {
maxTotal = _maxTotal;
}
function setTreasury(address _treasury) external onlyOwner {
treasury = _treasury;
}
function getPaymentTokens() public view returns (PaymentToken[] memory) {
return paymentTokens;
}
function resetPaymentTokens(
address[] calldata _tokens,
uint256[] calldata _prices
) external onlyOwner {
delete paymentTokens;
emit PaymentTokensReset();
for (uint256 i = 0; i < _tokens.length; i++) {
paymentTokens.push(PaymentToken(IERC20(_tokens[i]), _prices[i]));
emit PaymentTokenUpdated(_tokens[i], _prices[i]);
}
}
function addPaymentToken(
address _token,
uint256 _price
) external onlyOwner {
for (uint256 i = 0; i < paymentTokens.length; i++) {
if (address(paymentTokens[i].token) == _token) {
paymentTokens[i].price = _price;
emit PaymentTokenUpdated(_token, _price);
return;
}
}
paymentTokens.push(PaymentToken(IERC20(_token), _price));
emit PaymentTokenUpdated(_token, _price);
}
function removePaymentToken(address _token) external onlyOwner {
uint256 length = paymentTokens.length;
uint256 toRemove;
for (uint256 i = 0; i < length; i++) {
if (address(paymentTokens[i].token) == _token) {
toRemove = i;
}
}
paymentTokens[toRemove] = paymentTokens[length - 1];
paymentTokens.pop();
emit PaymentTokenUpdated(_token, 0);
}
function getPriceInToken(
address paymentToken
) public view returns (uint256) {
for (uint256 i = 0; i < paymentTokens.length; i++) {
if (address(paymentTokens[i].token) == paymentToken) {
return paymentTokens[i].price;
}
}
return 0;
}
function getAvailableAllocation(
address _address
) public view virtual returns (uint256) {
return getMaxAllocation(_address) - userInfo[_address].reward;
}
function buy(
address paymentToken,
uint256 paymentAmount
) external withAllocation(msg.sender) whenNotPaused nonReentrant {
require(block.timestamp >= startTime, "Not started");
require(block.timestamp <= endTime, "Ended");
uint256 allocationAmount = getTokenAmount(paymentToken, paymentAmount);
require(allocationAmount > 0, "Invalid amount");
total = total + allocationAmount;
require(total <= maxTotal, "Max total reached");
IERC20(paymentToken).safeTransferFrom(
msg.sender,
treasury,
paymentAmount
);
UserInfo storage user = userInfo[msg.sender];
uint256 totalUserAllocation = user.reward + allocationAmount;
require(
totalUserAllocation <= getMaxAllocation(msg.sender),
"Address allocation limit reached"
);
addUserReward(msg.sender, allocationAmount);
emit Bought(msg.sender, paymentToken, paymentAmount, allocationAmount);
}
function getTokenAmount(
address paymentToken,
uint256 paymentAmount
) public view returns (uint256) {
if (getPriceInToken(paymentToken) == 0) return 0;
return (paymentAmount * 1e18) / getPriceInToken(paymentToken);
}
function getMaxAllocation(
address _address
) public view virtual returns (uint256);
function getSaleData()
public
view
returns (
address _rewardToken,
uint256 _startTime,
uint256 _endTime,
uint256 _maxTotal,
uint256 _total,
uint256 _available,
PaymentToken[] memory _paymentTokens
)
{
return (
address(rewardToken),
startTime,
endTime,
maxTotal,
total,
maxTotal - total,
paymentTokens
);
}
function getUserData(
address _address
)
public
view
returns (uint256 _allocation, uint256 _reward, uint256 _available)
{
return (
getMaxAllocation(_address),
userInfo[_address].reward,
getAvailableAllocation(_address)
);
}
}
文件 11 的 16:MoonSaleCustom.sol
pragma solidity 0.8.19;
import "./MoonSaleBase.sol";
contract MoonSaleCustom is MoonSaleBase {
using SafeERC20 for IERC20;
mapping(address => uint256) public allocations;
constructor(
address _saleToken,
uint256 _price,
address _defaultPaymentToken,
uint256 _startTime,
uint256 _endTime,
uint256 _claimTime,
uint256 _maxTotal,
address _treasury,
uint256[4] memory _vestingData
)
MoonSaleBase(
_saleToken,
_price,
_defaultPaymentToken,
_startTime,
_endTime,
_claimTime,
_maxTotal,
_treasury,
_vestingData
)
{}
function getMaxAllocation(
address _address
) public view override returns (uint256) {
return allocations[_address];
}
function setAllocations(
address[] memory _addresses,
uint256[] memory _allocations
) external onlyOwner {
require(_addresses.length == _allocations.length, "length mismatch");
for (uint256 i = 0; i < _addresses.length; i++) {
allocations[_addresses[i]] = _allocations[i];
}
}
}
文件 12 的 16: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);
}
}
文件 13 的 16:Pausable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Pausable is Context {
event Paused(address account);
event Unpaused(address account);
bool private _paused;
constructor() {
_paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 14 的 16:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
文件 15 的 16:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
文件 16 的 16:VestedClaim.sol
pragma solidity 0.8.19;
import "./BaseClaim.sol";
contract VestedClaim is BaseClaim {
uint256 public constant BASE_POINTS = 10000;
uint256 public initialUnlock;
uint256 public cliff;
uint256 public vesting;
uint256 public vestingInterval;
constructor(address _rewardToken) BaseClaim(_rewardToken) {
require(initialUnlock <= BASE_POINTS, "initialUnlock too high");
initialUnlock = 2000;
cliff = 90 days;
vesting = 455 days;
vestingInterval = 1 days;
}
function calculateUnlockedAmount(
uint256 _totalAmount,
uint256 _timestamp
) internal view override returns (uint256) {
if (_timestamp < claimTime) {
return 0;
}
uint256 timeSinceClaim = _timestamp - claimTime;
uint256 unlockedAmount = 0;
if (timeSinceClaim <= cliff) {
unlockedAmount = (_totalAmount * initialUnlock) / BASE_POINTS;
} else if (timeSinceClaim > cliff + vesting) {
unlockedAmount = _totalAmount;
} else {
uint256 unlockedOnClaim = (_totalAmount * initialUnlock) /
BASE_POINTS;
uint256 vestable = _totalAmount - unlockedOnClaim;
uint256 intervalsSince = (timeSinceClaim - cliff) / vestingInterval;
uint256 totalVestingIntervals = vesting / vestingInterval;
unlockedAmount =
((vestable * intervalsSince) / totalVestingIntervals) +
unlockedOnClaim;
}
return unlockedAmount;
}
function totalAvailableAfter() public view override returns (uint256) {
return claimTime + cliff + vesting;
}
}
{
"compilationTarget": {
"contracts/sale/MoonSaleCustom.sol": "MoonSaleCustom"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_saleToken","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"address","name":"_defaultPaymentToken","type":"address"},{"internalType":"uint256","name":"_startTime","type":"uint256"},{"internalType":"uint256","name":"_endTime","type":"uint256"},{"internalType":"uint256","name":"_claimTime","type":"uint256"},{"internalType":"uint256","name":"_maxTotal","type":"uint256"},{"internalType":"address","name":"_treasury","type":"address"},{"internalType":"uint256[4]","name":"_vestingData","type":"uint256[4]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"paymentToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"paymentAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"buyAmount","type":"uint256"}],"name":"Bought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address[]","name":"claimants","type":"address[]"},{"indexed":true,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"ClaimantsAdded","type":"event"},{"anonymous":false,"inputs":[],"name":"ClaimsPaused","type":"event"},{"anonymous":false,"inputs":[],"name":"ClaimsUnpaused","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":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"PaymentTokenUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"PaymentTokensReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalWithdrawn","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address[]","name":"claimants","type":"address[]"}],"name":"RewardsFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"BASE_POINTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_claimants","type":"address[]"},{"internalType":"uint256[]","name":"_claimAmounts","type":"uint256[]"}],"name":"addClaimants","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"addPaymentToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allocations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"areClaimsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"paymentToken","type":"address"},{"internalType":"uint256","name":"paymentAmount","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cliff","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"tokenAddress","type":"address"}],"name":"emergencyWithdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_claimants","type":"address[]"}],"name":"freezeRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getAvailableAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getMaxAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPaymentTokens","outputs":[{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct MoonSaleBase.PaymentToken[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"paymentToken","type":"address"}],"name":"getPriceInToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSaleData","outputs":[{"internalType":"address","name":"_rewardToken","type":"address"},{"internalType":"uint256","name":"_startTime","type":"uint256"},{"internalType":"uint256","name":"_endTime","type":"uint256"},{"internalType":"uint256","name":"_maxTotal","type":"uint256"},{"internalType":"uint256","name":"_total","type":"uint256"},{"internalType":"uint256","name":"_available","type":"uint256"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct MoonSaleBase.PaymentToken[]","name":"_paymentTokens","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"paymentToken","type":"address"},{"internalType":"uint256","name":"paymentAmount","type":"uint256"}],"name":"getTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getUserData","outputs":[{"internalType":"uint256","name":"_allocation","type":"uint256"},{"internalType":"uint256","name":"_reward","type":"uint256"},{"internalType":"uint256","name":"_available","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getWithdrawableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialUnlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTotal","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":"pauseClaims","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"paymentTokens","outputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"removePaymentToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_prices","type":"uint256[]"}],"name":"resetPaymentTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_addresses","type":"address[]"},{"internalType":"uint256[]","name":"_allocations","type":"uint256[]"}],"name":"setAllocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_endTime","type":"uint256"}],"name":"setEndTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxTotal","type":"uint256"}],"name":"setMaxTotal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_startTime","type":"uint256"}],"name":"setStartTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"},{"internalType":"uint256[]","name":"_rewards","type":"uint256[]"},{"internalType":"uint256[]","name":"_withdrawns","type":"uint256[]"}],"name":"setUserInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"total","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAvailableAfter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalWithdrawn","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":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unPauseClaims","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_claimTime","type":"uint256"}],"name":"updateClaimTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"withdrawn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vesting","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingInterval","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"}]