以太坊合约开发ERC721 NFT

ERC721

ERC721 是以太坊上的一个标准协议,用于创建不可分割、不可互换的独特资产,如数字艺术品、游戏物品、收藏品等。每个 ERC721 资产都有一个唯一的标识符,被称为 token ID。这个标准协议定义了一组功能和事件,使得 ERC721 资产可以被安全地转移、拥有、交易和检索。与传统的货币不同,每个 ERC721 资产都是独特的,并且不可替代,因此它们可以用于代表任何种类的独特资产,并且具有非常广泛的应用。

Mint

可以在以下测试网络自己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

开发

Openzeppelin 的帮助下可以很快的入手开发ERC721标准的NFT合约。参考之前测试用的NFT代码,自行编译并且发布到了更多的测试网络上,可以很方便的进行ERC721 NFT测试。
引入ERC721库之后,自己只需要实现setBaseURI mint等自定义方法即可,非常简单。
合约开发使用的是VSCode和hardhat,合约验证使用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;
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容