编译器
0.8.23+commit.f704f362
文件 1 的 10:ERC20.sol
pragma solidity >=0.8.0;
abstract contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
文件 2 的 10:ERC721.sol
pragma solidity >=0.8.0;
abstract contract ERC721 {
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x5b5e139f;
}
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
文件 3 的 10:IERC721.sol
pragma solidity ^0.8.23;
interface IERC721 {
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function ownerOf(uint256 tokenId) external view returns (address owner);
}
文件 4 的 10:ILuckyGhouls.sol
pragma solidity ^0.8.23;
interface ILuckyGhouls {
function mintsPerWallet(address wallet) external view returns (uint256);
function setGoldenGhoulMinted() external;
function goldenGhoulMinted() external view returns (bool);
function withdrawRecipient() external view returns (address);
function royaltyRecipient() external view returns (address);
function isMintedOut() external view returns (bool);
function PRICE() external view returns (uint256);
function isEverythingClosed() external view returns (bool);
function getMintBlockNumber(
uint256 tokenId
) external view returns (uint256);
function getMintTimestamp(uint256 tokenId) external view returns (uint256);
}
文件 5 的 10:ILuckyGhoulsBonusSystem.sol
pragma solidity ^0.8.23;
interface ILuckyGhoulsBonusSystem {
struct BonusClaim {
address tokenAddress;
uint256 amount;
uint256 claimedAt;
}
function getBonusClaim(
uint256 tokenId
) external view returns (BonusClaim memory);
function getBonusClaims(
uint256[] calldata tokenIds
) external view returns (BonusClaim[] memory);
}
文件 6 的 10:LibString.sol
pragma solidity >=0.8.0;
library LibString {
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) return toString(uint256(value));
unchecked {
str = toString(uint256(-value));
assembly {
let length := mload(str)
mstore(str, 45)
str := sub(str, 1)
mstore(str, add(length, 1))
}
}
}
function toString(uint256 value) internal pure returns (string memory str) {
assembly {
let newFreeMemoryPointer := add(mload(0x40), 160)
mstore(0x40, newFreeMemoryPointer)
str := sub(newFreeMemoryPointer, 32)
mstore(str, 0)
let end := str
for { let temp := value } 1 {} {
str := sub(str, 1)
mstore8(str, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
str := sub(str, 32)
mstore(str, length)
}
}
}
文件 7 的 10:LuckyGhouls.sol
pragma solidity ^0.8.23;
import {ERC721} from "solmate/src/tokens/ERC721.sol";
import {ERC20} from "solmate/src/tokens/ERC20.sol";
import {LibString} from "solmate/src/utils/LibString.sol";
import {Owned} from "solmate/src/auth/Owned.sol";
import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol";
import {MerkleProofLib} from "solmate/src/utils/MerkleProofLib.sol";
import {ILuckyGhouls} from "./interfaces/ILuckyGhouls.sol";
import {IERC721} from "./interfaces/IERC721.sol";
import {ILuckyGhoulsBonusSystem} from "./interfaces/ILuckyGhoulsBonusSystem.sol";
contract LuckyGhouls is ERC721, Owned, ILuckyGhouls {
using LibString for uint256;
uint256 public immutable MAX_SUPPLY;
uint256 public immutable RESERVED_COUNT;
uint256 public PRICE;
uint256 public DISCOUNT_PRICE;
uint256 public immutable MAX_MINTS_PER_WALLET;
uint256 public publicMintStart;
uint256 public currentTokenId;
uint256 public totalMinted;
address public immutable withdrawRecipient;
address public immutable royaltyRecipient;
uint96 public constant ROYALTY_BPS = 690;
bool _isClosedOverride;
bool public goldenGhoulMinted;
bool public mintingActive;
ILuckyGhoulsBonusSystem public bonusSystem;
string public baseUri;
error ZeroQuantity();
error ExceedsMaxSupply();
error InsufficientPayment();
error EmptyBaseURI();
error TokenDoesNotExist();
error MaxSupplyExceeded();
error InvalidMerkleProof();
error MaxMintsPerWalletExceeded();
error NonExistentTokenURI();
error MintingClosed();
error Nope();
error PublicMintNotStarted();
error MintNotActive();
bytes32 public discountMerkleRoot;
bytes32 public freeMerkleRoot;
mapping(uint256 => uint256) private _mintBlockNumber;
mapping(uint256 => uint256) public _mintTimestamp;
mapping(address => bool) public hasUsedSpecialPrice;
mapping(address => uint256) public mintsPerWallet;
mapping(address => uint256[]) public lastMintedTokens;
enum SpecialPriceStatus {
None,
Free,
Discount
}
address public authorizedBonusSystem;
struct MintConfig {
uint256 totalPrice;
bool usesSpecialPrice;
SpecialPriceStatus specialPriceStatus;
bool hasUsedSpecialPrice;
uint256 mintsRemaining;
bool isMintedOut;
bool isEverythingClosed;
uint256 remainingSupply;
bool isPublicMintStarted;
}
constructor(
address _withdrawRecipient,
address _royaltyRecipient,
string memory initialBaseUri,
address owner,
uint256 maxSupply,
uint256 reservedCount,
uint256 price,
uint256 discountPrice,
uint256 maxMintsPerWallet
) ERC721("Lucky Ghouls", "LCKY") Owned(owner) {
withdrawRecipient = _withdrawRecipient;
royaltyRecipient = _royaltyRecipient;
if (bytes(initialBaseUri).length == 0) revert EmptyBaseURI();
baseUri = initialBaseUri;
currentTokenId = 1;
MAX_SUPPLY = maxSupply;
RESERVED_COUNT = reservedCount;
PRICE = price;
DISCOUNT_PRICE = discountPrice;
MAX_MINTS_PER_WALLET = maxMintsPerWallet;
for (uint256 i = 0; i < RESERVED_COUNT; i++) {
uint256 tokenId = MAX_SUPPLY - RESERVED_COUNT + 1 + i;
_mint(owner, tokenId);
totalMinted++;
_mintBlockNumber[tokenId] = block.number;
_mintTimestamp[tokenId] = block.timestamp;
}
}
modifier validatePayment(uint256 quantity, bytes32[] calldata merkleProof) {
(uint256 price, bool usesSpecialPrice) = getMintPrice(
msg.sender,
quantity,
merkleProof
);
if (msg.value < price) revert InsufficientPayment();
_;
}
modifier validateMint(uint256 quantity, bytes32[] calldata merkleProof) {
if (isEverythingClosed()) revert MintingClosed();
if (quantity == 0) revert ZeroQuantity();
if (isMintedOut() || willBeMintedOut(quantity)) {
revert MaxSupplyExceeded();
}
_;
}
modifier lockInEarlyAccessLists(bytes32[] calldata merkleProof) {
if (block.timestamp < publicMintStart) {
SpecialPriceStatus status = _checkSpecialPriceStatus(
msg.sender,
merkleProof
);
if (
status != SpecialPriceStatus.Free &&
status != SpecialPriceStatus.Discount
) revert PublicMintNotStarted();
}
_;
}
function setBonusSystem(address _bonusSystem) external onlyOwner {
bonusSystem = ILuckyGhoulsBonusSystem(_bonusSystem);
}
function updatePublicMintStart(
uint256 newPublicMintStart
) external onlyOwner {
publicMintStart = newPublicMintStart;
}
function setMintingActive(bool active) external onlyOwner {
mintingActive = active;
}
function _exists(uint256 tokenId) internal view returns (bool) {
return _ownerOf[tokenId] != address(0);
}
function updatePrices(
uint256 price,
uint256 discountPrice
) external onlyOwner {
PRICE = price;
DISCOUNT_PRICE = discountPrice;
}
function mint(
uint256 quantity,
bytes32[] calldata merkleProof
)
external
payable
lockInEarlyAccessLists(merkleProof)
validateMint(quantity, merkleProof)
validatePayment(quantity, merkleProof)
{
if (!mintingActive) revert MintNotActive();
if (mintsPerWallet[msg.sender] + quantity > MAX_MINTS_PER_WALLET) {
revert MaxMintsPerWalletExceeded();
}
(, bool usesSpecialPrice) = getMintPrice(
msg.sender,
quantity,
merkleProof
);
if (usesSpecialPrice) {
hasUsedSpecialPrice[msg.sender] = true;
}
delete lastMintedTokens[msg.sender];
unchecked {
mintsPerWallet[msg.sender] += quantity;
for (uint256 i; i < quantity; ++i) {
_mint(msg.sender, currentTokenId);
_mintBlockNumber[currentTokenId] = block.number;
_mintTimestamp[currentTokenId] = block.timestamp;
lastMintedTokens[msg.sender].push(currentTokenId);
currentTokenId++;
totalMinted++;
}
}
}
function setBaseURI(string calldata newBaseUri) external onlyOwner {
if (bytes(newBaseUri).length == 0) revert EmptyBaseURI();
baseUri = newBaseUri;
}
function withdraw() external {
SafeTransferLib.safeTransferETH(
withdrawRecipient,
address(this).balance
);
}
function withdrawERC20(address token) external onlyOwner {
uint256 balance = ERC20(token).balanceOf(address(this));
SafeTransferLib.safeTransfer(ERC20(token), withdrawRecipient, balance);
}
function withdrawERC721(
address token,
address to,
uint256 tokenId
) external onlyOwner {
IERC721(token).safeTransferFrom(address(this), to, tokenId);
}
function supportsInterface(
bytes4 interfaceId
) public pure virtual override(ERC721) returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x5b5e139f ||
interfaceId == 0x2a55205a;
}
function royaltyInfo(
uint256,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount) {
return (royaltyRecipient, (salePrice * ROYALTY_BPS) / 10000);
}
function tokenURI(
uint256 id
) public view virtual override returns (string memory) {
if (!_exists(id)) revert TokenDoesNotExist();
return string.concat(baseUri, id.toString(), ".json");
}
function mintsRemaining(address wallet) external view returns (uint256) {
return MAX_MINTS_PER_WALLET - mintsPerWallet[wallet];
}
function getMintBlockNumber(
uint256 tokenId
) external view returns (uint256) {
if (!_exists(tokenId)) revert TokenDoesNotExist();
return _mintBlockNumber[tokenId];
}
function getMintTimestamp(uint256 tokenId) external view returns (uint256) {
if (!_exists(tokenId)) revert TokenDoesNotExist();
return _mintTimestamp[tokenId];
}
function isMintedOut() public view returns (bool) {
return totalMinted >= MAX_SUPPLY;
}
function willBeMintedOut(uint256 additionalQty) public view returns (bool) {
return totalMinted + additionalQty > MAX_SUPPLY;
}
function remainingSupply() public view returns (uint256) {
return MAX_SUPPLY - totalMinted;
}
function setDiscountMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
discountMerkleRoot = _merkleRoot;
}
function setFreeMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
freeMerkleRoot = _merkleRoot;
}
function _checkSpecialPriceStatus(
address user,
bytes32[] calldata merkleProof
) internal view returns (SpecialPriceStatus) {
if (
freeMerkleRoot != bytes32(0) &&
MerkleProofLib.verify(
merkleProof,
freeMerkleRoot,
keccak256(abi.encodePacked(user))
)
) {
return SpecialPriceStatus.Free;
}
if (
discountMerkleRoot != bytes32(0) &&
MerkleProofLib.verify(
merkleProof,
discountMerkleRoot,
keccak256(abi.encodePacked(user))
)
) {
return SpecialPriceStatus.Discount;
}
return SpecialPriceStatus.None;
}
function getMintPrice(
address user,
uint256 quantity,
bytes32[] calldata merkleProof
) public view returns (uint256 totalPrice, bool usesSpecialPrice) {
if (hasUsedSpecialPrice[user]) {
return (PRICE * quantity, false);
}
SpecialPriceStatus status = _checkSpecialPriceStatus(user, merkleProof);
if (status == SpecialPriceStatus.Free) {
return (PRICE * (quantity - 1), true);
}
if (status == SpecialPriceStatus.Discount) {
return (DISCOUNT_PRICE + (PRICE * (quantity - 1)), true);
}
return (PRICE * quantity, false);
}
function getUserSpecialPriceStatus(
address user,
bytes32[] calldata merkleProof
) public view returns (SpecialPriceStatus status, bool hasUsedPrice) {
hasUsedPrice = hasUsedSpecialPrice[user];
status = _checkSpecialPriceStatus(user, merkleProof);
return (status, hasUsedPrice);
}
function getLastMintedTokens(
address wallet
) external view returns (uint256[] memory) {
return lastMintedTokens[wallet];
}
function getClaimedBonusesForLastMintedTokens(
address wallet
)
external
view
returns (
ILuckyGhoulsBonusSystem.BonusClaim[] memory claims,
uint256[] memory tokenIds
)
{
tokenIds = lastMintedTokens[wallet];
claims = bonusSystem.getBonusClaims(tokenIds);
return (claims, tokenIds);
}
function setMintingClosed(bool closed) external onlyOwner {
_isClosedOverride = closed;
}
function isEverythingClosed() public view returns (bool) {
return _isClosedOverride || goldenGhoulMinted;
}
function setAuthorizedBonusSystem(
address _authorizedBonusSystem
) external onlyOwner {
authorizedBonusSystem = _authorizedBonusSystem;
}
function setGoldenGhoulMinted() external {
if (msg.sender != owner && msg.sender != authorizedBonusSystem)
revert Nope();
goldenGhoulMinted = true;
}
function getMintConfig(
address user,
uint256 quantity,
bytes32[] calldata merkleProof
) external view returns (MintConfig memory) {
(uint256 totalPrice, bool usesSpecialPrice) = getMintPrice(
user,
quantity,
merkleProof
);
(
SpecialPriceStatus specialPriceStatus,
bool _hasUsedSpecialPrice
) = getUserSpecialPriceStatus(user, merkleProof);
return
MintConfig({
totalPrice: totalPrice,
usesSpecialPrice: usesSpecialPrice,
specialPriceStatus: specialPriceStatus,
hasUsedSpecialPrice: _hasUsedSpecialPrice,
mintsRemaining: MAX_MINTS_PER_WALLET - mintsPerWallet[user],
isMintedOut: isMintedOut(),
isEverythingClosed: isEverythingClosed(),
remainingSupply: remainingSupply(),
isPublicMintStarted: block.timestamp >= publicMintStart
});
}
receive() external payable {}
}
文件 8 的 10:MerkleProofLib.sol
pragma solidity >=0.8.0;
library MerkleProofLib {
function verify(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool isValid) {
assembly {
if proof.length {
let end := add(proof.offset, shl(5, proof.length))
let offset := proof.offset
for {} 1 {} {
let leafSlot := shl(5, gt(leaf, calldataload(offset)))
mstore(leafSlot, leaf)
mstore(xor(leafSlot, 32), calldataload(offset))
leaf := keccak256(0, 64)
offset := add(offset, 32)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
}
文件 9 的 10:Owned.sol
pragma solidity >=0.8.0;
abstract contract Owned {
event OwnershipTransferred(address indexed user, address indexed newOwner);
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
文件 10 的 10:SafeTransferLib.sol
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
library SafeTransferLib {
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 68), amount)
success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "APPROVE_FAILED");
}
}
{
"compilationTarget": {
"src/LuckyGhouls.sol": "LuckyGhouls"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":ds-test/=lib/solmate/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":solmate/=lib/solmate/"
]
}
[{"inputs":[{"internalType":"address","name":"_withdrawRecipient","type":"address"},{"internalType":"address","name":"_royaltyRecipient","type":"address"},{"internalType":"string","name":"initialBaseUri","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"reservedCount","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"discountPrice","type":"uint256"},{"internalType":"uint256","name":"maxMintsPerWallet","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EmptyBaseURI","type":"error"},{"inputs":[],"name":"ExceedsMaxSupply","type":"error"},{"inputs":[],"name":"InsufficientPayment","type":"error"},{"inputs":[],"name":"InvalidMerkleProof","type":"error"},{"inputs":[],"name":"MaxMintsPerWalletExceeded","type":"error"},{"inputs":[],"name":"MaxSupplyExceeded","type":"error"},{"inputs":[],"name":"MintNotActive","type":"error"},{"inputs":[],"name":"MintingClosed","type":"error"},{"inputs":[],"name":"NonExistentTokenURI","type":"error"},{"inputs":[],"name":"Nope","type":"error"},{"inputs":[],"name":"PublicMintNotStarted","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"ZeroQuantity","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DISCOUNT_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_MINTS_PER_WALLET","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESERVED_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROYALTY_BPS","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_mintTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"authorizedBonusSystem","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bonusSystem","outputs":[{"internalType":"contract ILuckyGhoulsBonusSystem","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"discountMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"freeMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"getClaimedBonusesForLastMintedTokens","outputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"claimedAt","type":"uint256"}],"internalType":"struct ILuckyGhoulsBonusSystem.BonusClaim[]","name":"claims","type":"tuple[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"getLastMintedTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getMintBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"getMintConfig","outputs":[{"components":[{"internalType":"uint256","name":"totalPrice","type":"uint256"},{"internalType":"bool","name":"usesSpecialPrice","type":"bool"},{"internalType":"enum LuckyGhouls.SpecialPriceStatus","name":"specialPriceStatus","type":"uint8"},{"internalType":"bool","name":"hasUsedSpecialPrice","type":"bool"},{"internalType":"uint256","name":"mintsRemaining","type":"uint256"},{"internalType":"bool","name":"isMintedOut","type":"bool"},{"internalType":"bool","name":"isEverythingClosed","type":"bool"},{"internalType":"uint256","name":"remainingSupply","type":"uint256"},{"internalType":"bool","name":"isPublicMintStarted","type":"bool"}],"internalType":"struct LuckyGhouls.MintConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"getMintPrice","outputs":[{"internalType":"uint256","name":"totalPrice","type":"uint256"},{"internalType":"bool","name":"usesSpecialPrice","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getMintTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"getUserSpecialPriceStatus","outputs":[{"internalType":"enum LuckyGhouls.SpecialPriceStatus","name":"status","type":"uint8"},{"internalType":"bool","name":"hasUsedPrice","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"goldenGhoulMinted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"hasUsedSpecialPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isEverythingClosed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMintedOut","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"lastMintedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintingActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mintsPerWallet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"mintsRemaining","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicMintStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"remainingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"royaltyRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_authorizedBonusSystem","type":"address"}],"name":"setAuthorizedBonusSystem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newBaseUri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_bonusSystem","type":"address"}],"name":"setBonusSystem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setDiscountMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setFreeMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setGoldenGhoulMinted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"active","type":"bool"}],"name":"setMintingActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"closed","type":"bool"}],"name":"setMintingClosed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"discountPrice","type":"uint256"}],"name":"updatePrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPublicMintStart","type":"uint256"}],"name":"updatePublicMintStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"additionalQty","type":"uint256"}],"name":"willBeMintedOut","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdrawERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]