EthereumEthereum
0x99...FBD0
VAYC V2

VAYC V2

VAYC

收藏品
大小
1,147
收藏品
所有者
264
23% 独特的所有者
此合同的源代码已经过验证!
合同元数据
编译器
0.8.7+commit.e28d00a7
语言
Solidity
合同源代码
文件 1 的 2:SparkleMarket.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.0;

interface IERC721M {
    function marketTransferFrom(address from, address to, uint256 tokenId) external;
    function ownerOf(uint256 tokenId) external view returns (address owner);
    function isApprovedForAll(address owner, address operator) external view returns (bool);
    function getApproved(uint tokenId) external view returns (address approved);
}

contract SparkleMarket {

    IERC721M public nftContract;

    uint256 constant NEVER_EXPIRE = 0;
    address constant ANYBODY = address(0);
    uint64 constant THE_PAST = 1;

    struct Listing {
        uint256 price;
        address from;
        address to;
        uint64 expireTime;
    }

    mapping(uint256 => Listing) private listings;

    event CreateListing(
        uint256 indexed tokenID,
        uint256 price,
        address indexed from,
        address indexed to,
        uint64 expireTime
    );

    event CancelListing(address indexed from, uint256 indexed tokenID);

    event Sale(
        uint256 indexed tokenID,
        uint256 price,
        address indexed from,
        address indexed to
    );

    constructor(address _nftContract) {
        nftContract = IERC721M(_nftContract);
    }

    function _list(uint256 tokenId, uint256 price, address to, uint64 expireTime) internal {
        _requireOwnerOrAllowed(tokenId, msg.sender);
        require(expireTime > block.timestamp || expireTime == NEVER_EXPIRE, "Gotta let the listing have a valid expiration time!");
        listings[tokenId] = Listing({price: price, from: nftContract.ownerOf(tokenId), to: to, expireTime: expireTime });
        emit CreateListing(tokenId, price, msg.sender, to, expireTime);
    }

    function listInWei(uint256 tokenId, uint256 priceInWeiNotEth, address to, uint64 expireTime) external {
        _list(tokenId, priceInWeiNotEth, to, expireTime);
    }

    function listBatchInWei(
        uint256[] calldata tokenIdList,
        uint256[] calldata priceListInWeiNotEth,
        address[] calldata toList,
        uint64[] calldata expireTimeList) external {
        for (uint i = 0; i < tokenIdList.length; i++) {
            _list(tokenIdList[i], priceListInWeiNotEth[i], toList[i], expireTimeList[i]);
        }
    }

    function _requireOwnerOrAllowed(uint256 tokenId, address theAddress) internal view {
        address tokenOwner = nftContract.ownerOf(tokenId);
        require(tokenOwner == theAddress
            || nftContract.isApprovedForAll(tokenOwner, theAddress)
            || nftContract.getApproved(tokenId) == theAddress,
            "Address is not owner or approved");
    }

    function cancelListing(uint256[] calldata tokenIdList) external {
        for (uint i = 0; i < tokenIdList.length; i++) {
            uint256 tokenId = tokenIdList[i];
            _requireOwnerOrAllowed(tokenId, msg.sender);
            listings[tokenId].expireTime = THE_PAST;
            emit CancelListing(msg.sender, tokenId);
        }
    }

    function buy(uint256 tokenId) external payable {
        Listing memory l = listings[tokenId];
        require(l.from != address(0) &&
                (l.price != 0 ||
                l.to != address(0)),
                "Cannot buy an uninitialized listing");
        require(l.expireTime > block.timestamp
            || l.expireTime == NEVER_EXPIRE,
            "Listing must still be valid to be sold");
        require(l.price == msg.value, "must send correct money to pay purchase price");
        require(l.from == nftContract.ownerOf(tokenId), "Owner must still own for listing to be valid");
        if (l.to != ANYBODY)
          require(l.to == msg.sender, "if private sale, buyer must match target address the seller wants to sell to");

        listings[tokenId].expireTime = THE_PAST;
        address payable from = payable(nftContract.ownerOf(tokenId));
        nftContract.marketTransferFrom(from, msg.sender, tokenId);
        from.transfer(msg.value);
        emit Sale(tokenId, msg.value, from, msg.sender);
    }

    function buyBatch(uint256[] calldata tokenIdList) external payable {
        uint sum = 0;
        for(uint i = 0; i < tokenIdList.length; i++) {
            uint tokenId = tokenIdList[i];
            Listing memory l = listings[tokenId];
            require(l.expireTime != 0 ||
                    l.price != 0 ||
                    l.to != address(0),
                    "Cannot buy an uninitialized listing");
            require(l.expireTime > block.timestamp
                || l.expireTime == NEVER_EXPIRE,
                "Listing must still be valid to be sold");
            require(l.from == nftContract.ownerOf(tokenId), "Owner must still own for listing to be valid");
            if (l.to != ANYBODY)
              require(l.to == msg.sender, "if private sale, buyer must match target address the seller wants to sell to");
            sum += l.price;
        }

        require(sum == msg.value, "must send correct money to pay purchase price");

        for(uint i = 0; i < tokenIdList.length; i++) {
            uint tokenId = tokenIdList[i];
            listings[tokenId].expireTime = THE_PAST;
            address payable from = payable(nftContract.ownerOf(tokenId));
            nftContract.marketTransferFrom(from, msg.sender, tokenId);
            from.transfer(listings[tokenId].price);
        }

    }

    function getListing(uint256 tokenId) external view returns (Listing memory) {
        return listings[tokenId];
    }

    function isForSale(uint256 tokenId) external view returns (bool) {
        if ( listings[tokenId].from != nftContract.ownerOf(tokenId) )
            return false;
        if ( listings[tokenId].expireTime > block.timestamp)
            return true;
        bool listingInitialized = (listings[tokenId].price != 0 || listings[tokenId].to != ANYBODY);
        if (listings[tokenId].expireTime == NEVER_EXPIRE && listingInitialized)
            return true;
        return false;
    }

}
合同源代码
文件 2 的 2:VAYC.sol
// 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
/// Unnecessary functions have been deleted, mint, safeMint and burn
/// Added our own mint functions, tweaked ownerOf to support our weirdness

import "./SparkleMarket.sol";

interface IERC721 {
    function ownerOf(uint256 tokenId) external view returns (address owner);
    function setOwnerOf(uint256 id, address newOwner) 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 VAYC {
    /*///////////////////////////////////////////////////////////////
                                 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);

    /*///////////////////////////////////////////////////////////////
                          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 public name;

    string public symbol;

    address private admin;
    uint16 public totalSupply;
    uint16 public counter = 0;
    uint16 public constant  MAX_SUPPLY =  10000; // only first 10000 were minted

    IERC721 private MAYC = IERC721(0x60E4d786628Fea6478F785A6d7e704777c86a7c6);
    IERC721 private BAYC = IERC721(0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D);
    IERC721 private BAKC = IERC721(0xba30E5F9Bb24caa003E9f2f0497Ad287FDF95623);
    //function setMAYC(address _mayc) public {MAYC = IERC721(_mayc);} //helper for unit testing
    //IERC721 private MAYC = IERC721(0x6A8e25D0168B98e240d28a803e71ada93973F856);
    //IERC721 private BAYC = IERC721(0x6A8e25D0168B98e240d28a803e71ada93973F856);
    //IERC721 private BAKC = IERC721(address(0xdead));

    SparkleMarket public market;
    uint256 public constant COST_MAYC =   0.042069 ether;
    uint256 public constant COST_PUBLIC = 0.069420 ether;
    uint8 constant MAX_MINT = 10;

   enum SaleStatus {
       Paused,
       Presale,
       Whitelist,
       Public
    }
    SaleStatus public saleMode;


    /*///////////////////////////////////////////////////////////////
                            ERC721 STORAGE                        
    //////////////////////////////////////////////////////////////*/

    mapping(address => uint256) public balanceOf;

    mapping(uint256 => address) private preOf;

    mapping(uint256 => address) private publicOf;

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*///////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function ownerOf(uint256 id) public view returns (address) {
        if (preOf[id] != address(0))
            return preOf[id];
        else
            return publicOf[id];
    }

    function approve(address spender, uint256 id) external {
        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) 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]++;
        }

        if (preOf[id] != address(0))
            preOf[id] = to;
        else
            publicOf[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"
        );
    }

    /*///////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f;   // ERC165 Interface ID for ERC721Metadata
    }

    modifier onlyAdmin() {
        require(msg.sender == admin, "must be admin");
        _;
    }

    /*///////////////////////////////////////////////////////////////
                       VAYC SPECIFIC LOGIC
    //////////////////////////////////////////////////////////////*/
    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
        admin = msg.sender;
        market = new SparkleMarket(address(this));
    }

    function saleToPause() external onlyAdmin {
        saleMode = SaleStatus.Paused;
    }

    function saleToPre() external onlyAdmin {
        saleMode = SaleStatus.Presale;
    }

    function saleToWhitelist() external onlyAdmin {
        saleMode = SaleStatus.Whitelist;
    }

    function saleToPublic() external onlyAdmin {
        saleMode = SaleStatus.Public;
    }

    function withdraw() external onlyAdmin {
        payable(admin).transfer(address(this).balance);
    }

    function mintPre(uint[] calldata tokenIds) external payable {
        require(saleMode == SaleStatus.Presale, NOT_LIVE);
        require(msg.value == COST_MAYC*tokenIds.length, INCORRECT_PRICE);
        require(totalSupply + tokenIds.length < MAX_SUPPLY, MINTED_OUT);
        for (uint i = 0; i < tokenIds.length; i++) {
            require(msg.sender == MAYC.ownerOf(tokenIds[i]), "Missing required token");
            require(tokenIds[i] < MAX_SUPPLY, "TokenId too high");
        }
        for (uint i = 0; i < tokenIds.length; i++) {
            require(ownerOf(tokenIds[i]) == address(0), "ALREADY_MINTED");

            // Counter overflow is incredibly unrealistic.
            unchecked {
                balanceOf[msg.sender]++;
            }

            preOf[tokenIds[i]] = msg.sender;
            emit Transfer(address(0), msg.sender, tokenIds[i]);
        }
        unchecked{
            totalSupply = totalSupply + (uint16(tokenIds.length));
        }
    }

    function mintWL(uint16 num, uint tokenId) external payable {
        require(saleMode == SaleStatus.Whitelist, NOT_LIVE);
        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 mintPublic(uint16 num) external payable {
        require(saleMode == SaleStatus.Public, NOT_LIVE);
        require(msg.value == COST_PUBLIC * num, INCORRECT_PRICE);
        _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 (preOf[id] == address(0)) {
                publicOf[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;
        }
    }

    // This function is here as a fallback in case we get undesirable gas consumption due to the
    // structure of the preOf array. if it does, the owner can pause the contract, mint the offending
    // token id and push public supply up to what it needs to be to get over O(n) SREAD operations in
    // the MAYC Array.
    // We may use it for promotions & giveaways.
    // One can monitor the deployment address for suspicious activity if you do not trust the devs.
    function mintAdmin(uint id, uint16 supplyOverwrite) external onlyAdmin {
        unchecked {
            publicOf[id] = msg.sender;
            counter = supplyOverwrite;
            balanceOf[msg.sender] += 1;
            totalSupply += 1;
        }
        emit Transfer(address(0), msg.sender, id);
    }

    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 tokenURI(uint256 id) external view returns (string memory) {
        if(ownerOf(id) == address(0))
            return "";
        return string(abi.encodePacked(string(abi.encodePacked("ipfs://QmUupQShChuEV9r6qEKGLpd51qWj4zGtMzkpsCDTuUutJQ/", uintToString(id))), ".json")); // TODO CHANGE MEEEEEE
    }

    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]++;
        }

        if (preOf[id] != address(0))
            preOf[id] = to;
        else
            publicOf[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": {
    "VAYC.sol": "VAYC"
  },
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"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":"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":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"contract SparkleMarket","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":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint16","name":"supplyOverwrite","type":"uint16"}],"name":"mintAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"mintPre","outputs":[],"stateMutability":"payable","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":[{"internalType":"uint256","name":"id","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":"enum VAYC.SaleStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"saleToPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"saleToPre","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"saleToPublic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"saleToWhitelist","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":"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":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]