以太坊合約開(kāi)發(fā)ERC721 NFT

ERC721

ERC721 是以太坊上的一個(gè)標(biāo)準(zhǔn)協(xié)議,用于創(chuàng)建不可分割、不可互換的獨(dú)特資產(chǎn),如數(shù)字藝術(shù)品、游戲物品、收藏品等。每個(gè) ERC721 資產(chǎn)都有一個(gè)唯一的標(biāo)識(shí)符,被稱為 token ID。這個(gè)標(biāo)準(zhǔn)協(xié)議定義了一組功能和事件,使得 ERC721 資產(chǎn)可以被安全地轉(zhuǎn)移、擁有、交易和檢索。與傳統(tǒng)的貨幣不同,每個(gè) ERC721 資產(chǎn)都是獨(dú)特的,并且不可替代,因此它們可以用于代表任何種類的獨(dú)特資產(chǎn),并且具有非常廣泛的應(yīng)用。

Mint

可以在以下測(cè)試網(wǎng)絡(luò)自己mint,具體步驟可以參考 Github

Network Contract Address
Ethereum Speolia 0x8b6c7d03bd8911236feaa43afacbb8b0e563d93a
BNB Chain Testnet 0xfD9585145A5BC2D7A74c82E78aB9314723683BA8
Polygon Mumbai 0x9B1AAb1492c375F011811cBdBd88FFEf3ce2De76
Avalanche Testnet 0x825706F885445867958A3f3a4dec8189e0E02524
Arbitrum Goerli 0x1E2c6DD54b473B63112416850E73B2F567DDE3Df
Optimism Goerili 0x825706f885445867958a3f3a4dec8189e0e02524
Base Testnet 0x155afc7fbaedda178c6185ec9151964b9719828b

開(kāi)發(fā)

Openzeppelin 的幫助下可以很快的入手開(kāi)發(fā)ERC721標(biāo)準(zhǔn)的NFT合約。參考之前測(cè)試用的NFT代碼,自行編譯并且發(fā)布到了更多的測(cè)試網(wǎng)絡(luò)上,可以很方便的進(jìn)行ERC721 NFT測(cè)試。
引入ERC721庫(kù)之后,自己只需要實(shí)現(xiàn)setBaseURI mint等自定義方法即可,非常簡(jiǎn)單。
合約開(kāi)發(fā)使用的是VSCode和hardhat,合約驗(yàn)證使用hardhat flattening。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract TestToadz is ERC721, Ownable {
    uint256 public constant maxTokens = 6969;
    uint256 public numAvailableTokens = 6969;
    uint256 public constant maxMintsPerTx = 5;
    mapping(address => uint256) public addressToNumOwned;
    string private _contractURI;
    bool public devMintLocked = false;
    uint256[10000] private _availableTokens;

    constructor() ERC721("TestToadz", "TESTTOADZ") {}

    // metadata URI
    string private _baseTokenURI =
        "ipfs://QmWEFSMku6yGLQ9TQr66HjSd9kay8ZDYKbBEfjNi4pLtrr/";

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function tokenURI(uint256 _serialId)
        public
        view
        override
        returns (string memory)
    {
        string memory base = _baseURI();
        string memory _tokenURI = Strings.toString(_serialId);

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }

        return string(abi.encodePacked(base, _tokenURI));
    }

    function getNumAvailableTokens() public view returns (uint256) {
        return numAvailableTokens;
    }

    function setBaseURI(string calldata baseURI) external onlyOwner {
        _baseTokenURI = baseURI;
    }

    //Minting
    function mint(uint256 quantity) public {
        uint256 updatedNumAvailableTokens = numAvailableTokens;
        require(
            block.timestamp >= 1337133769,
            "Sale starts at whatever this time is"
        );
        require(
            quantity <= maxMintsPerTx,
            "There is a limit on minting too many at a time!"
        );
        require(
            updatedNumAvailableTokens - quantity >= 0,
            "Minting this many would exceed supply!"
        );
        require(
            addressToNumOwned[msg.sender] + quantity <= 40,
            "Can't own more than 20 toadz"
        );
        require(msg.sender == tx.origin, "No contracts!");
        for (uint256 i = 0; i < quantity; i++) {
            uint256 tokenId = getRandomSerialToken(quantity, i);
            _safeMint(msg.sender, tokenId);
            updatedNumAvailableTokens--;
        }
        numAvailableTokens = updatedNumAvailableTokens;
        addressToNumOwned[msg.sender] =
            addressToNumOwned[msg.sender] +
            quantity;
    }

    //Dev mint special tokens
    function mintSpecial(uint256[] memory specialIds) external onlyOwner {
        require(!devMintLocked, "Dev Mint Permanently Locked");
        uint256 num = specialIds.length;
        for (uint256 i = 0; i < num; i++) {
            uint256 specialId = specialIds[i];
            _safeMint(msg.sender, specialId);
        }
    }

    function getRandomSerialToken(uint256 _numToFetch, uint256 _i)
        internal
        returns (uint256)
    {
        uint256 randomNum = uint256(
            keccak256(
                abi.encode(
                    msg.sender,
                    tx.gasprice,
                    block.number,
                    block.timestamp,
                    blockhash(block.number - 1),
                    _numToFetch,
                    _i
                )
            )
        );
        uint256 randomIndex = randomNum % numAvailableTokens;
        uint256 valAtIndex = _availableTokens[randomIndex];
        uint256 result;
        if (valAtIndex == 0) {
            result = randomIndex;
        } else {
            result = valAtIndex;
        }

        uint256 lastIndex = numAvailableTokens - 1;
        if (randomIndex != lastIndex) {
            uint256 lastValInArray = _availableTokens[lastIndex];
            if (lastValInArray == 0) {
                _availableTokens[randomIndex] = lastIndex;
            } else {
                _availableTokens[randomIndex] = lastValInArray;
            }
        }

        numAvailableTokens--;
        return result;
    }

    function lockDevMint() public onlyOwner {
        devMintLocked = true;
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容