编译器
0.8.20+commit.a1b79de6
文件 1 的 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);
}
}
}
文件 2 的 5:ERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
abstract contract ERC20 is IERC20 {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
constructor(string memory name_, string memory symbol_, uint8 decimals_) {
_name = name_;
_symbol = symbol_;
_decimals = decimals_;
}
function name() public view virtual returns (string memory) {
return _name;
}
function symbol() public view virtual returns (string memory) {
return _symbol;
}
function decimals() public view virtual returns (uint8) {
return _decimals;
}
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = msg.sender;
_transfer(owner, to, value);
return true;
}
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = msg.sender;
_approve(owner, spender, value);
return true;
}
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = msg.sender;
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
_totalSupply -= value;
}
} else {
unchecked {
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
文件 3 的 5:ERC20FreeMintSigned.sol
pragma solidity ^0.8.20;
import {ERC20} from "../ERC20.sol";
import {IERC20} from "../IERC20.sol";
import {ECDSA} from "../utils/cryptography/ECDSA.sol";
import {IERC20FreeMintSigned} from "./IERC20FreeMintSigned.sol";
contract ERC20FreeMintSigned is ERC20, IERC20FreeMintSigned {
bool public launched;
uint256 public immutable amountPerMint;
uint256 public immutable mintableCount;
uint256 public immutable mintStartTime;
uint256 public maxAllowedMints;
mapping(address => uint256) public userTotalMints;
address public immutable factory;
mapping (address => bool) public signers;
mapping(bytes32 => bool) public usedNonces;
event Mint(address indexed to, uint256 amount);
error MintNotStarted();
error ERC20NotLaunched();
error ERC20AlreadyLaunched();
error NotFactory();
error InvalidSigner();
error NonceAlreadyUsed();
error MaxMintsReached();
constructor(
string memory name_, string memory symbol_, uint8 decimals_,
uint256 amountPerMint_, uint256 mintableCount_,
uint256 maxAllowedMints_, uint256 mintStartTime_,
address factory_
) ERC20(name_, symbol_, decimals_) {
require(amountPerMint_ > 0, "Invalid amount per mint");
require(mintableCount_ > 0, "Invalid mintable count");
amountPerMint = amountPerMint_;
mintableCount = mintableCount_;
maxAllowedMints = maxAllowedMints_;
mintStartTime = mintStartTime_;
factory = factory_;
}
function mintableOf(address account) external view returns (uint256) {
if (launched) return 0;
return super.balanceOf(account);
}
function getMetadata() external view returns (string memory, string memory, uint8) {
return (name(), symbol(), decimals());
}
function getStaticInfo() external view returns (uint256, uint256, uint256) {
return (amountPerMint, mintableCount, mintStartTime);
}
function getDynamicInfo() external view returns (bool, uint256) {
return (launched, super.totalSupply());
}
function mint(
uint256 nonce, address receiver,
bytes calldata signature
) external {
if (block.timestamp < mintStartTime) revert MintNotStarted();
if (launched) revert ERC20AlreadyLaunched();
bytes memory message = abi.encode(block.chainid, address(this), nonce, receiver);
address _recoveredSigner = ECDSA.recover(keccak256(message), signature);
if (!signers[_recoveredSigner]) revert InvalidSigner();
bytes32 _usedNonce = keccak256(abi.encodePacked(_recoveredSigner, nonce));
if (usedNonces[_usedNonce]) revert NonceAlreadyUsed();
usedNonces[_usedNonce] = true;
if (maxAllowedMints > 0 && userTotalMints[receiver] >= maxAllowedMints)
revert MaxMintsReached();
++userTotalMints[receiver];
_mint(receiver, amountPerMint);
emit Mint(receiver, amountPerMint);
if (super.totalSupply() == mintableCount * amountPerMint) {
launched = true;
}
}
function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
if (!launched) return 0;
return super.totalSupply();
}
function balanceOf(address account) public view override(ERC20, IERC20) returns (uint256) {
if (!launched) return 0;
return super.balanceOf(account);
}
function _update(address from, address to, uint256 value) internal override {
super._update(from, to, value);
if (!launched && from != address(0)) revert ERC20NotLaunched();
}
function setSigner(address signer, bool valid) external {
if (msg.sender != factory) revert NotFactory();
signers[signer] = valid;
}
function setMaxAllowedMints(uint256 newMaxAllowedMints) external {
if (msg.sender != factory) revert NotFactory();
maxAllowedMints = newMaxAllowedMints;
}
}
文件 4 的 5:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
error ERC20InvalidSender(address sender);
error ERC20InvalidReceiver(address receiver);
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
error ERC20InvalidApprover(address approver);
error ERC20InvalidSpender(address spender);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
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 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 5 的 5:IERC20FreeMintSigned.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
interface IERC20FreeMintSigned is IERC20 {
function launched() external view returns (bool);
function amountPerMint() external view returns (uint256);
function mintableCount() external view returns (uint256);
function mintStartTime() external view returns (uint256);
function maxAllowedMints() external view returns (uint256);
function userTotalMints(address account) external view returns (uint256);
function factory() external view returns (address);
function signers(address signer) external view returns (bool);
function usedNonces(bytes32 hashedSignerNonce) external view returns (bool);
function mintableOf(address account) external view returns (uint256);
function getMetadata() external view returns (string memory, string memory, uint8);
function getStaticInfo() external view returns (uint256, uint256, uint256);
function getDynamicInfo() external view returns (bool, uint256);
function mint(uint256 nonce, address receiver, bytes calldata signature) external;
function setSigner(address signer, bool valid) external;
function setMaxAllowedMints(uint256 newMaxAllowedMints) external;
}
{
"compilationTarget": {
"contracts/ERC20/ERC20FreeMintSigned.sol": "ERC20FreeMintSigned"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"uint256","name":"amountPerMint_","type":"uint256"},{"internalType":"uint256","name":"mintableCount_","type":"uint256"},{"internalType":"uint256","name":"maxAllowedMints_","type":"uint256"},{"internalType":"uint256","name":"mintStartTime_","type":"uint256"},{"internalType":"address","name":"factory_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"ERC20AlreadyLaunched","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[],"name":"ERC20NotLaunched","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"MaxMintsReached","type":"error"},{"inputs":[],"name":"MintNotStarted","type":"error"},{"inputs":[],"name":"NonceAlreadyUsed","type":"error"},{"inputs":[],"name":"NotFactory","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amountPerMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDynamicInfo","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMetadata","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStaticInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"launched","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAllowedMints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintableCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"mintableOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxAllowedMints","type":"uint256"}],"name":"setMaxAllowedMints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"bool","name":"valid","type":"bool"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"signers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"usedNonces","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userTotalMints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]