// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// This file is forked from Solmate v6,
/// We stand on the shoulders of giants
interface IERC721 {
function ownerOf(uint256 tokenId) external view returns (address owner);
}
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
/// @dev Note that balanceOf does not revert if passed the zero address, in defiance of the ERC.
contract VAYC2 {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
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);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string private constant NOT_LIVE = "Sale not live";
string private constant INCORRECT_PRICE = "Gotta pay right money";
string private constant MINTED_OUT = "Max supply reached";
string private constant FROZEN = "DATA_FROZEN";
string private constant MIGRATED = "MIGRATION_OVER";
string private token_metadata = "ipfs://QmUp2pBkqiGhdztfs5ym3AGttA5L8JLAUEPRawEKinRVcJ/";
string public name;
string public symbol;
address public owner;
uint16 public totalSupply;
uint16 public counter = 0;
uint16 public constant MAX_SUPPLY = 10000; // only first 10000 were minted
bool public saleMode = false;
bool public market_frozen = false;
bool public metadata_frozen = false;
bool public migration_over = false;
uint8 public giveawaysMinted = 0;
address public market = address(0); //initialize to an address nobody controls
uint256 public constant COST_MAYC = 0.042069 ether;
uint256 public constant COST_PUBLIC = 0.069420 ether;
uint8 constant MAX_MINT = 10;
uint8 constant GIVEAWAY_LIMIT = 100;
IERC721 private MAYC = IERC721(0x60E4d786628Fea6478F785A6d7e704777c86a7c6);
IERC721 private BAYC = IERC721(0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D);
IERC721 private BAKC = IERC721(0xba30E5F9Bb24caa003E9f2f0497Ad287FDF95623);
IERC721 private OLD_VAYC = IERC721(0x99FE9b46e8e2559EAc3c7BD5dd8f55238D89FBD0);
/*///////////////////////////////////////////////////////////////
ERC721 STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => uint256) public balanceOf;
mapping(uint256 => address) public ownerOf;
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*///////////////////////////////////////////////////////////////
ERC721/165/173 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) external {
address tokenOwner = ownerOf[id];
require(msg.sender == tokenOwner || isApprovedForAll[tokenOwner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) external {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public {
require(from == ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || msg.sender == getApproved[id] || isApprovedForAll[from][msg.sender],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
balanceOf[from]--;
balanceOf[to]++;
}
ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) external {
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 memory data
) external {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function transferOwnership(address _newOwner) external onlyOwner {
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaces
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 tokens
interfaceId == 0x7f5828d0 || // ERC173 Interface ID for ERC173 ownership
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*///////////////////////////////////////////////////////////////
VAYC SPECIFIC LOGIC
//////////////////////////////////////////////////////////////*/
//
// Modifiers
//
modifier onlyOwner() {
require(msg.sender == owner, "Ownable: caller is not the owner");
_;
}
modifier duringMigration() {
require(migration_over == false, MIGRATED);
_;
}
modifier duringSale() {
require(saleMode == true, NOT_LIVE);
_;
}
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
function setMarket(address _market) external onlyOwner {
require(market_frozen == false, FROZEN);
market_frozen = true;
market = _market;
}
function saleToPause() external onlyOwner {
saleMode = false;
}
function saleToPublic() external onlyOwner {
saleMode = true;
}
function withdraw(uint amount) external onlyOwner {
if(amount == 0) {
payable(owner).transfer(address(this).balance);
} else {
payable(owner).transfer(amount);
}
}
//
// Minting
//
function mintPublic(uint16 num) external payable duringSale {
require(msg.value == COST_PUBLIC * num, INCORRECT_PRICE);
_mintY(num);
}
function mintWL(uint16 num, uint tokenId) external payable duringSale {
bool baycFan =
(msg.sender == MAYC.ownerOf(tokenId)) ||
(msg.sender == BAYC.ownerOf(tokenId)) ||
(msg.sender == BAKC.ownerOf(tokenId));
require(baycFan, "Not whitelisted");
require(msg.value == COST_MAYC * num, INCORRECT_PRICE);
_mintY(num);
}
function mintGiveaway(uint8 num) external onlyOwner {
require(giveawaysMinted < GIVEAWAY_LIMIT, "No more giveaways");
giveawaysMinted += num;
_mintY(num);
}
function _mintY(uint16 num) internal {
require(num <= MAX_MINT, "Max 10 per TX");
require(totalSupply + num < MAX_SUPPLY, MINTED_OUT);
require(msg.sender.code.length == 0, "Hack harder bot master"); // bypassable, but raises level of effort
uint id = counter;
uint num_already_minted = 0;
while(num_already_minted < num){
if (ownerOf[id] == address(0)) {
ownerOf[id] = msg.sender;
emit Transfer(address(0), msg.sender, id);
num_already_minted += 1;
}
id += 1;
}
unchecked {
balanceOf[msg.sender] += num;
counter = uint16(id);
totalSupply = totalSupply + num;
}
}
//
// Migration logic
//
function _migrateTo(uint id, address person) internal {
ownerOf[id] = person;
emit Transfer(address(0), msg.sender, id);
balanceOf[person] += 1;
}
function endMigration() public onlyOwner {
migration_over = true;
}
function setMigrationSupply(uint16 _supply, uint16 _counter) public onlyOwner duringMigration {
totalSupply = _supply;
counter = _counter;
}
function migrateByHand(uint[] calldata tokenIds) public onlyOwner duringMigration {
for (uint i; i < tokenIds.length; i++) {
_migrateTo(tokenIds[i], OLD_VAYC.ownerOf(tokenIds[i]));
}
}
function migrateBulk(uint16 start, uint16 end) public onlyOwner duringMigration {
for (uint16 i = start; i < end; i++) {
_migrateTo(i, OLD_VAYC.ownerOf(i));
}
}
//
// TokenURI logic
//
function uintToString(uint256 value) internal pure returns (string memory) {
// stolen from OpenZeppelin Strings library
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function setTokenMetadata(string calldata url) public onlyOwner {
require(metadata_frozen == false, FROZEN);
metadata_frozen = true;
token_metadata = url;
}
function tokenURI(uint256 id) external view returns (string memory) {
if(ownerOf[id] == address(0))
return "";
return string(abi.encodePacked(string(abi.encodePacked(token_metadata, uintToString(id))), ".json"));
}
//
// Market Integration
//
function marketTransferFrom(address from, address to, uint256 id) external {
require(msg.sender == address(market), "INVALID_CALLER");
require(to != address(0), "INVALID_RECIPIENT");
unchecked {
balanceOf[from]--;
balanceOf[to]++;
}
ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
interface ERC721TokenReceiver {
function onERC721Received(
address operator,
address from,
uint256 id,
bytes calldata data
) external returns (bytes4);
}
{
"compilationTarget": {
"VAYC2.sol": "VAYC2"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"previousOwner","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":"COST_MAYC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COST_PUBLIC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"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":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"counter","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"giveawaysMinted","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"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":"market","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":"marketTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"market_frozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadata_frozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"start","type":"uint16"},{"internalType":"uint16","name":"end","type":"uint16"}],"name":"migrateBulk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"migrateByHand","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migration_over","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"num","type":"uint8"}],"name":"mintGiveaway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"num","type":"uint16"}],"name":"mintPublic","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"num","type":"uint16"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mintWL","outputs":[],"stateMutability":"payable","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":"","type":"uint256"}],"name":"ownerOf","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":[],"name":"saleMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"saleToPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"saleToPublic","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":"_market","type":"address"}],"name":"setMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_supply","type":"uint16"},{"internalType":"uint16","name":"_counter","type":"uint16"}],"name":"setMigrationSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"url","type":"string"}],"name":"setTokenMetadata","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":"totalSupply","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"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":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]