编译器
0.8.27+commit.40a35a09
文件 1 的 5:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 2 的 5:ECDSA.sol
pragma solidity ^0.8.20;
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
error ECDSAInvalidSignature();
error ECDSAInvalidSignatureLength(uint256 length);
error ECDSAInvalidSignatureS(bytes32 s);
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return;
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
文件 3 的 5:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 4 的 5:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 5 的 5:TokenSale.sol
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract TokenSale is Ownable, ReentrancyGuard {
uint208 public hardcap;
address public beneficiary;
address public allowanceSigner;
uint48 public startTimestamp;
uint48 public endTimestamp;
uint208 public totalInvested;
uint48 public investmentId = 1;
uint256[] public paginationBlocksBy1000;
error NoDirectDepositsAllowed();
error ZeroAddressForbidden();
error PaymentTooSmall();
error SaleHasNotStarted();
error SaleHasEnded();
error SaleIsFull();
error OnlyFutureEndTimestampAllowed();
error StartTimestampMustBeBeforeEndTimestamp();
error HardcapMustBeGreaterThanZero();
error HardcapMustBeGreaterThanTotalInvested();
error InvalidAllowanceSignature();
error RefundTransferFailed();
error BeneficiaryTransferFailed();
event Investment(bytes16 indexed userId, uint208 investedAmount, uint208 saleProgressBefore, uint48 investmentId, uint48 timestamp);
constructor(address beneficiary_, address owner_, address allowanceSigner_, uint48 startTimestamp_, uint48 endTimestamp_, uint208 hardcap_) Ownable(owner_) {
require(beneficiary_ != address(0), ZeroAddressForbidden());
require(allowanceSigner_ != address(0), ZeroAddressForbidden());
require(endTimestamp_ > block.timestamp, OnlyFutureEndTimestampAllowed());
require(startTimestamp_ < endTimestamp_, StartTimestampMustBeBeforeEndTimestamp());
require(hardcap_ > 0, HardcapMustBeGreaterThanZero());
beneficiary = beneficiary_;
allowanceSigner = allowanceSigner_;
startTimestamp = startTimestamp_;
endTimestamp = endTimestamp_;
hardcap = hardcap_;
}
receive() external payable {
revert NoDirectDepositsAllowed();
}
fallback() external payable {
revert NoDirectDepositsAllowed();
}
function invest(bytes16 userId, bytes memory allowanceSignature) external payable nonReentrant {
require(msg.value >= 10**15, PaymentTooSmall());
require(block.timestamp >= startTimestamp, SaleHasNotStarted());
require(block.timestamp <= endTimestamp, SaleHasEnded());
bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n16", userId));
address signer = ECDSA.recover(messageHash, allowanceSignature);
require(signer == allowanceSigner, InvalidAllowanceSignature());
uint208 acceptedAmount = 0;
uint208 availableSpace = hardcap - totalInvested;
if (msg.value > availableSpace) {
acceptedAmount = availableSpace;
} else {
acceptedAmount = uint208(msg.value);
}
require(acceptedAmount > 0, SaleIsFull());
uint256 refundAmount = msg.value - acceptedAmount;
if (refundAmount > 0) {
(bool refundSuccess, ) = msg.sender.call{value: refundAmount}("");
require(refundSuccess, RefundTransferFailed());
}
(bool success, ) = beneficiary.call{value: uint256(acceptedAmount)}("");
require(success, BeneficiaryTransferFailed());
if (investmentId % 1000 == 0) {
paginationBlocksBy1000.push(block.number);
}
emit Investment(userId, acceptedAmount, totalInvested, investmentId, uint48(block.timestamp));
totalInvested += acceptedAmount;
investmentId++;
}
function setBeneficiary(address newBeneficiary) external onlyOwner {
require(newBeneficiary != address(0), ZeroAddressForbidden());
beneficiary = newBeneficiary;
}
function setAllowanceSigner(address newAllowanceSigner) external onlyOwner {
require(newAllowanceSigner != address(0), ZeroAddressForbidden());
allowanceSigner = newAllowanceSigner;
}
function setStartAndEndTimestamp(uint48 newStartTimestamp, uint48 newEndTimestamp) external onlyOwner {
require(newStartTimestamp < newEndTimestamp, StartTimestampMustBeBeforeEndTimestamp());
startTimestamp = newStartTimestamp;
endTimestamp = newEndTimestamp;
}
function setHardcap(uint208 newHardcap) external onlyOwner {
require(newHardcap >= totalInvested, HardcapMustBeGreaterThanTotalInvested());
hardcap = newHardcap;
}
}
{
"compilationTarget": {
"contracts/TokenSale.sol": "TokenSale"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"beneficiary_","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"allowanceSigner_","type":"address"},{"internalType":"uint48","name":"startTimestamp_","type":"uint48"},{"internalType":"uint48","name":"endTimestamp_","type":"uint48"},{"internalType":"uint208","name":"hardcap_","type":"uint208"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BeneficiaryTransferFailed","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[],"name":"HardcapMustBeGreaterThanTotalInvested","type":"error"},{"inputs":[],"name":"HardcapMustBeGreaterThanZero","type":"error"},{"inputs":[],"name":"InvalidAllowanceSignature","type":"error"},{"inputs":[],"name":"NoDirectDepositsAllowed","type":"error"},{"inputs":[],"name":"OnlyFutureEndTimestampAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"PaymentTooSmall","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RefundTransferFailed","type":"error"},{"inputs":[],"name":"SaleHasEnded","type":"error"},{"inputs":[],"name":"SaleHasNotStarted","type":"error"},{"inputs":[],"name":"SaleIsFull","type":"error"},{"inputs":[],"name":"StartTimestampMustBeBeforeEndTimestamp","type":"error"},{"inputs":[],"name":"ZeroAddressForbidden","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes16","name":"userId","type":"bytes16"},{"indexed":false,"internalType":"uint208","name":"investedAmount","type":"uint208"},{"indexed":false,"internalType":"uint208","name":"saleProgressBefore","type":"uint208"},{"indexed":false,"internalType":"uint48","name":"investmentId","type":"uint48"},{"indexed":false,"internalType":"uint48","name":"timestamp","type":"uint48"}],"name":"Investment","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"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"allowanceSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"beneficiary","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endTimestamp","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hardcap","outputs":[{"internalType":"uint208","name":"","type":"uint208"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes16","name":"userId","type":"bytes16"},{"internalType":"bytes","name":"allowanceSignature","type":"bytes"}],"name":"invest","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"investmentId","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"paginationBlocksBy1000","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAllowanceSigner","type":"address"}],"name":"setAllowanceSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newBeneficiary","type":"address"}],"name":"setBeneficiary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint208","name":"newHardcap","type":"uint208"}],"name":"setHardcap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"newStartTimestamp","type":"uint48"},{"internalType":"uint48","name":"newEndTimestamp","type":"uint48"}],"name":"setStartAndEndTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTimestamp","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalInvested","outputs":[{"internalType":"uint208","name":"","type":"uint208"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]