文件 1 的 12:Address.sol
pragma solidity ^0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) private pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 12: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 的 12:EnumerableSet.sol
pragma solidity ^0.8.0;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping(bytes32 => uint256) _indexes;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastvalue = set._values[lastIndex];
set._values[toDeleteIndex] = lastvalue;
set._indexes[lastvalue] = valueIndex;
}
set._values.pop();
delete set._indexes[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
struct Bytes32Set {
Set _inner;
}
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
文件 4 的 12:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 5 的 12:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
文件 6 的 12:IERC721Enumerable.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Enumerable is IERC721 {
function totalSupply() external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
function tokenByIndex(uint256 index) external view returns (uint256);
}
文件 7 的 12:ISipherNFT.sol
pragma solidity 0.8.4;
import {IERC721Enumerable} from '@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol';
interface ISipherNFT is IERC721Enumerable {
function rollStartIndex() external;
function mintGenesis(uint256 amount, address to, uint256 unitPrice) external;
function mintFork(uint256 tokenId) external;
function originals(uint256 forkId)
external
view
returns (uint256 originalId);
function genesisMinter() external view returns (address);
function forkMinter() external view returns (address);
function randomizedStartIndex() external view returns (uint256);
function currentId() external view returns (uint256);
function baseSipherURI() external view returns (string memory);
function contractURI() external view returns (string memory);
}
文件 8 的 12:ISipherNFTSale.sol
pragma solidity 0.8.4;
import {ISipherNFT} from '../interfaces/ISipherNFT.sol';
interface ISipherNFTSale {
struct SaleConfig {
uint64 publicTime;
uint64 publicEndTime;
uint64 privateTime;
uint64 freeMintTime;
uint64 endTime;
uint32 maxSupply;
}
struct SaleRecord {
uint32 totalSold;
uint32 ownerBought;
uint32 totalPublicSold;
uint32 totalWhitelistSold;
uint32 totalFreeMintSold;
}
struct UserRecord {
uint32 publicBought;
uint32 whitelistBought;
uint32 freeMintBought;
}
function buy(uint32 amount,uint32 privateCap, uint32 freeMintCap, bytes32[] memory proofs) external payable;
function rollStartIndex() external;
function getSaleConfig() external view returns (SaleConfig memory config);
function getSaleRecord() external view returns (SaleRecord memory record);
function getUserRecord(address user) external view returns (UserRecord memory record);
function merkleRoot() external view returns (bytes32);
function nft() external view returns (ISipherNFT);
}
文件 9 的 12:IWhitelist.sol
pragma solidity 0.8.4;
interface IWhitelist {
event SetWhitelistedMerkleRoot(bytes32 whitelistedMerkelRoot);
function setWhitelistedMerkleRoot(bytes32 _whitelistedRoot, uint32 _totalWhitelisted) external;
function isWhitelistedAddress(address buyer, uint32 privateCap, uint32 freeMintCap , bytes32[] memory proofs) external view returns (bool);
}
文件 10 的 12: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() {
_setOwner(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_setOwner(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 11 的 12:SipherNFTSale.sol
pragma solidity 0.8.4;
import {Address} from '@openzeppelin/contracts/utils/Address.sol';
import {ISipherNFT} from '../interfaces/ISipherNFT.sol';
import {ISipherNFTSale} from '../interfaces/ISipherNFTSale.sol';
import {Whitelist} from '../utils/Whitelist.sol';
contract SipherNFTSale is ISipherNFTSale, Whitelist {
using Address for address;
uint32 public constant MAX_OWNER_BOUGHT_INITIAL = 500;
uint32 public constant PUBLIC_SALE_CAP_PER_ADDRESS = 5;
uint32 public constant REDUCE_PRICE_INTERVAL = 600;
uint256 public constant REDUCE_PRICE_LEVEL = 50000000000000000;
uint256 public constant SALE_BASE_PRICE = 100000000000000000;
uint256 public constant SALE_PUBLIC_STARTING_PRICE = 900000000000000000;
bytes32 public override merkleRoot;
ISipherNFT public immutable override nft;
SaleRecord internal _saleRecord;
SaleConfig internal _saleConfig;
mapping(address => UserRecord) internal _userRecord;
event OwnerBought(address indexed buyer, uint32 amount, uint256 amountWeiPaid);
event PrivateBought(address indexed buyer, uint32 amount, uint256 amountWeiPaid);
event FreeMintBought(address indexed buyer, uint32 amount, uint256 amountWeiPaid);
event PublicBought(address indexed buyer, uint32 amount, uint256 amountWeiPaid);
event WithdrawSaleFunds(address indexed recipient, uint256 amount);
event RollStartIndex(address indexed trigger);
event UpdateSaleEndTime(uint64 endTime);
event SetMerkleRoot(bytes32 merkelRoot);
event Refund(address buyer, uint256 refundAmount);
constructor(
ISipherNFT _nft,
uint64 _publicTime,
uint64 _publicEndTime,
uint64 _privateTime,
uint64 _freeMintTime,
uint64 _endTime,
uint32 _maxSupply
) {
nft = _nft;
_saleConfig = SaleConfig({
publicTime: _publicTime,
publicEndTime: _publicEndTime,
privateTime: _privateTime,
freeMintTime: _freeMintTime,
endTime: _endTime,
maxSupply: _maxSupply
});
}
function withdrawSaleFunds(address payable recipient, uint256 amount) external onlyOwner {
(bool success, ) = recipient.call{value: amount}('');
require(success, 'SipherNFTSale: withdraw funds failed');
emit WithdrawSaleFunds(recipient, amount);
}
function setMerkleRoot(bytes32 _root) external onlyOwner {
require(
_blockTimestamp() < _saleConfig.publicTime,
'SipherNFTSale: only update before whitelist buy time'
);
require(_root != bytes32(0), 'SipherNFTSale: invalid root');
require(merkleRoot == bytes32(0), 'SipherNFTSale: already set merkle root');
merkleRoot = _root;
emit SetMerkleRoot(_root);
}
function getPublicSaleCurrentPrice() public view returns (uint256 currentPrice) {
uint256 timestamp = _blockTimestamp();
uint256 publicStartTime = _saleConfig.publicTime;
uint256 publicEndTime = _saleConfig.publicEndTime;
uint256 priceInterval = REDUCE_PRICE_INTERVAL;
if (timestamp < publicStartTime) {
currentPrice = SALE_PUBLIC_STARTING_PRICE;
return currentPrice;
} else if (timestamp >= publicStartTime && timestamp < publicEndTime) {
uint256 i = 0;
while ((publicStartTime + i * priceInterval) <= timestamp && i < 17) {
i++;
}
currentPrice = SALE_PUBLIC_STARTING_PRICE - (i - 1) * REDUCE_PRICE_LEVEL;
return currentPrice;
} else {
currentPrice = SALE_BASE_PRICE;
return currentPrice;
}
}
function buy(
uint32 amount,
uint32 privateCap,
uint32 freeMintCap,
bytes32[] memory proofs
) external payable override {
address buyer = msg.sender;
require(
(!buyer.isContract() && buyer == tx.origin) || buyer == owner(),
'SipherNFTSale: only EOA or owner'
);
require(merkleRoot != bytes32(0), 'SipherNFTSale: merkle root is not set yet');
uint256 unitPrice = getPublicSaleCurrentPrice();
_validateAndUpdateWithBuyAmount(buyer, amount, privateCap, freeMintCap, unitPrice, proofs);
nft.mintGenesis(amount, buyer, unitPrice);
}
function rollStartIndex() external override {
require(_blockTimestamp() > _saleConfig.endTime, 'SipherNFTSale: sale not ended');
address sender = msg.sender;
require(
(!sender.isContract() && sender == tx.origin) || sender == owner(),
'SipherNFTSale: only EOA or owner'
);
require(merkleRoot != bytes32(0), 'SipherNFTSale: merkle root is not set yet');
nft.rollStartIndex();
emit RollStartIndex(sender);
}
function updateSaleConfigTime(
uint64 _publicTime,
uint64 _publicEndTime,
uint64 _privateTime,
uint64 _freeMintTime,
uint64 _endTime
) external onlyOwner {
require(_publicTime >= _saleConfig.publicTime, 'SipherNFTSale: Invalid sale time input');
_saleConfig.publicTime = _publicTime;
_saleConfig.publicEndTime = _publicEndTime;
_saleConfig.privateTime = _privateTime;
_saleConfig.freeMintTime = _freeMintTime;
_saleConfig.endTime = _endTime;
emit UpdateSaleEndTime(_endTime);
}
function getSaleConfig() external view override returns (SaleConfig memory config) {
config = _saleConfig;
}
function getMaxPublicSaleCap() external view returns (uint32) {
return _saleConfig.maxSupply - totalWhitelisted;
}
function getSaleRecord() external view override returns (SaleRecord memory record) {
record = _saleRecord;
}
function getUserRecord(address user) external view override returns (UserRecord memory record) {
record = _userRecord[user];
}
function _validateAndUpdateWithBuyAmount(
address buyer,
uint32 amount,
uint32 privateCap,
uint32 freeMintCap,
uint256 unitPrice,
bytes32[] memory proofs
) internal {
SaleConfig memory config = _saleConfig;
require(
_saleRecord.totalSold + amount <= _saleConfig.maxSupply,
'SipherNFTSale: max supply reached'
);
address owner = owner();
uint256 totalPaid = msg.value;
uint256 timestamp = _blockTimestamp();
uint256 costToMint = unitPrice * amount;
if (buyer == owner) {
if (timestamp <= config.endTime) {
require(
_saleRecord.ownerBought + amount <= MAX_OWNER_BOUGHT_INITIAL,
'SipherNFTSale: max owner initial reached'
);
}
_saleRecord.ownerBought += amount;
_saleRecord.totalSold += amount;
emit OwnerBought(buyer, amount, totalPaid);
return;
}
require(config.publicTime <= timestamp, 'SipherNFTSale: Public Sale not started');
require(timestamp <= config.endTime, 'SipherNFTSale: already ended');
if (config.publicTime <= timestamp && timestamp < config.publicEndTime) {
require(
_saleRecord.totalPublicSold + amount <= (config.maxSupply - totalWhitelisted),
'SipherNFTSale: max public sale supply reached'
);
require(
_userRecord[buyer].publicBought + amount <= PUBLIC_SALE_CAP_PER_ADDRESS,
'SipherNFTSale: normal cap reached'
);
require(
(totalPaid >= costToMint) && (costToMint >= SALE_BASE_PRICE),
'SipherNFTSale: invalid paid value'
);
_saleRecord.totalPublicSold += amount;
_userRecord[buyer].publicBought += amount;
_saleRecord.totalSold += amount;
if (msg.value > costToMint) {
Address.sendValue(payable(msg.sender), msg.value - costToMint);
emit Refund(buyer, msg.value - costToMint);
}
emit PublicBought(buyer, amount, totalPaid);
return;
}
if (config.publicEndTime <= timestamp && timestamp < config.freeMintTime) {
require(
config.privateTime <= timestamp && timestamp < config.freeMintTime,
'SipherNFTSale: Private Sale not started'
);
require(totalPaid == amount * SALE_BASE_PRICE, 'SipherNFTSale: invalid paid value');
require(
_userRecord[buyer].whitelistBought + amount <= privateCap,
'SipherNFTSale: whitelisted private sale cap reached'
);
require(
isWhitelistedAddress(buyer, privateCap, freeMintCap, proofs) &&
whitelistedMerkelRoot != bytes32(0),
'SipherNFTSale: only whitelisted buyer'
);
_saleRecord.totalWhitelistSold += amount;
_userRecord[buyer].whitelistBought += amount;
_saleRecord.totalSold += amount;
emit PrivateBought(buyer, amount, totalPaid);
return;
}
if (config.freeMintTime <= timestamp && timestamp < config.endTime) {
require(
config.freeMintTime <= timestamp && timestamp < config.endTime,
'SipherNFTSale: Free Mint not started'
);
require(totalPaid == 0, 'Invalid paid amount');
require(
isWhitelistedAddress(buyer, privateCap, freeMintCap, proofs) &&
whitelistedMerkelRoot != bytes32(0),
'SipherNFTSale: only whitelisted buyer'
);
require(
_userRecord[buyer].freeMintBought + amount <= freeMintCap,
'SipherNFTSale: free mint cap reached'
);
_saleRecord.totalFreeMintSold += amount;
_userRecord[buyer].freeMintBought += amount;
_saleRecord.totalSold += amount;
emit FreeMintBought(buyer, amount, totalPaid);
return;
}
}
function _blockTimestamp() internal view returns (uint256) {
return block.timestamp;
}
}
文件 12 的 12:Whitelist.sol
pragma solidity 0.8.4;
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import {IWhitelist} from '../interfaces/IWhitelist.sol';
contract Whitelist is IWhitelist, Ownable {
bytes32 public whitelistedMerkelRoot;
uint32 public totalWhitelisted;
function setWhitelistedMerkleRoot(bytes32 _whitelistedRoot, uint32 _totalWhitelisted)
external
override
onlyOwner
{
require(_whitelistedRoot != bytes32(0), 'SipherNFTSale: invalid root');
require(_totalWhitelisted < 10000, 'Whiteist: max whitelisted is 9999 ');
whitelistedMerkelRoot = _whitelistedRoot;
totalWhitelisted = _totalWhitelisted;
emit SetWhitelistedMerkleRoot(_whitelistedRoot);
}
function isWhitelistedAddress(
address buyer,
uint32 privateCap,
uint32 freeMintCap,
bytes32[] memory proofs
) public view override returns (bool) {
require(whitelistedMerkelRoot != bytes32(0));
bytes32 computedHash = keccak256(abi.encode(buyer, privateCap, freeMintCap));
for (uint256 i = 0; i < proofs.length; i++) {
bytes32 proofElement = proofs[i];
if (computedHash < proofElement) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
return computedHash == whitelistedMerkelRoot;
}
}
{
"compilationTarget": {
"contracts/sale/SipherNFTSale.sol": "SipherNFTSale"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 22000
},
"remappings": []
}
[{"inputs":[{"internalType":"contract ISipherNFT","name":"_nft","type":"address"},{"internalType":"uint64","name":"_publicTime","type":"uint64"},{"internalType":"uint64","name":"_publicEndTime","type":"uint64"},{"internalType":"uint64","name":"_privateTime","type":"uint64"},{"internalType":"uint64","name":"_freeMintTime","type":"uint64"},{"internalType":"uint64","name":"_endTime","type":"uint64"},{"internalType":"uint32","name":"_maxSupply","type":"uint32"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint32","name":"amount","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amountWeiPaid","type":"uint256"}],"name":"FreeMintBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint32","name":"amount","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amountWeiPaid","type":"uint256"}],"name":"OwnerBought","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":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint32","name":"amount","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amountWeiPaid","type":"uint256"}],"name":"PrivateBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint32","name":"amount","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amountWeiPaid","type":"uint256"}],"name":"PublicBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"refundAmount","type":"uint256"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trigger","type":"address"}],"name":"RollStartIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"merkelRoot","type":"bytes32"}],"name":"SetMerkleRoot","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"whitelistedMerkelRoot","type":"bytes32"}],"name":"SetWhitelistedMerkleRoot","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"endTime","type":"uint64"}],"name":"UpdateSaleEndTime","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawSaleFunds","type":"event"},{"inputs":[],"name":"MAX_OWNER_BOUGHT_INITIAL","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PUBLIC_SALE_CAP_PER_ADDRESS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REDUCE_PRICE_INTERVAL","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REDUCE_PRICE_LEVEL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SALE_BASE_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SALE_PUBLIC_STARTING_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"amount","type":"uint32"},{"internalType":"uint32","name":"privateCap","type":"uint32"},{"internalType":"uint32","name":"freeMintCap","type":"uint32"},{"internalType":"bytes32[]","name":"proofs","type":"bytes32[]"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getMaxPublicSaleCap","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPublicSaleCurrentPrice","outputs":[{"internalType":"uint256","name":"currentPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSaleConfig","outputs":[{"components":[{"internalType":"uint64","name":"publicTime","type":"uint64"},{"internalType":"uint64","name":"publicEndTime","type":"uint64"},{"internalType":"uint64","name":"privateTime","type":"uint64"},{"internalType":"uint64","name":"freeMintTime","type":"uint64"},{"internalType":"uint64","name":"endTime","type":"uint64"},{"internalType":"uint32","name":"maxSupply","type":"uint32"}],"internalType":"struct ISipherNFTSale.SaleConfig","name":"config","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSaleRecord","outputs":[{"components":[{"internalType":"uint32","name":"totalSold","type":"uint32"},{"internalType":"uint32","name":"ownerBought","type":"uint32"},{"internalType":"uint32","name":"totalPublicSold","type":"uint32"},{"internalType":"uint32","name":"totalWhitelistSold","type":"uint32"},{"internalType":"uint32","name":"totalFreeMintSold","type":"uint32"}],"internalType":"struct ISipherNFTSale.SaleRecord","name":"record","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserRecord","outputs":[{"components":[{"internalType":"uint32","name":"publicBought","type":"uint32"},{"internalType":"uint32","name":"whitelistBought","type":"uint32"},{"internalType":"uint32","name":"freeMintBought","type":"uint32"}],"internalType":"struct ISipherNFTSale.UserRecord","name":"record","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint32","name":"privateCap","type":"uint32"},{"internalType":"uint32","name":"freeMintCap","type":"uint32"},{"internalType":"bytes32[]","name":"proofs","type":"bytes32[]"}],"name":"isWhitelistedAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nft","outputs":[{"internalType":"contract ISipherNFT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollStartIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_root","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_whitelistedRoot","type":"bytes32"},{"internalType":"uint32","name":"_totalWhitelisted","type":"uint32"}],"name":"setWhitelistedMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalWhitelisted","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_publicTime","type":"uint64"},{"internalType":"uint64","name":"_publicEndTime","type":"uint64"},{"internalType":"uint64","name":"_privateTime","type":"uint64"},{"internalType":"uint64","name":"_freeMintTime","type":"uint64"},{"internalType":"uint64","name":"_endTime","type":"uint64"}],"name":"updateSaleConfigTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"whitelistedMerkelRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawSaleFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]