solidity实现ERC721代币标准发布NFT

news2025/1/19 11:22:22

文章目录

  • 1、非同质化货币(NFT)- 维基百科
  • 2、IERC165
  • 3、IERC721
  • 4、IERC721Receiver
  • 5、IERC721Metadata
  • 6、ERC721
  • 7、ERC721 NFT 的实现
  • 8、编译部署

1、非同质化货币(NFT)- 维基百科

非同质化代币(英语:Non-Fungible Token,简称:NFT),是一种众筹扶持项目方的方式,也是区块链(数位账本)上的一种数据单位,每个代币可以代表一个独特的数字资料,作为虚拟商品所有权的电子认证或证书。由于其不能互换的特性,非同质化代币可以代表数字资产,如画作、艺术品、声音、影片、游戏中的项目或其他形式的创意作品。虽然作品本身是可以无限复制的,但这些代表它们的代币在其底层区块链上能被完整追踪,故能为买家提供所有权证明。诸如以太币、比特币等加密货币都有自己的代币标准以定义对NFT的使用。

2、IERC165

ERC165 是一个非常简单的以太坊标准,主要用于检测该合约是否支持查询的接口。调用者只需要传入想要检测的接口的ID(如ERC-165的ID是0x01ffc9a7),该函数以布尔值的方式(true支持,false不支持)告诉调用者该合约是否实现了这个接口。
IERC165 是 ERC165 标准的接口合约,规定了ERC165要实现的基本函数 supportsInterface

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


interface IERC165 {
    // ERC165 代币标准就是检查一个智能合约是不是支持了例如 ERC721 ,ERC1155 的接口。 
    // 如果支持,返回true
    function supportsInterface(
        bytes4 interfaceId
    ) external view returns (bool);
}

3、IERC721

IERC721 是 ERC721 标准的接口规范,它定义和规范了一个标准 ERC721 代币合约应该实现的功能。这里让 ERC721 合约直接继承自 IERC721() 接口。

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

import "./IERC165.sol";

// ERC721标准接口.
interface IERC721 is IERC165 {
    // 转账时触发
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    // 授权时触发 
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    // 在批量授权时触发 
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    
    // 返回某地址的NFT持有量balance
    function balanceOf(address owner) external view returns (uint256 balance);
    // 返回某 tokenId 的主人 owner
    function ownerOf(uint256 tokenId) external view returns (address owner);
    // 实现了 ERC721Receiver 接口的安全转账和重载函数  
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    function safeTransferFrom(address from,address to,uint256 tokenId) external;
    // 普通转账,参数为转出地址from,接收地址to和tokenId。
    function transferFrom(address from,address to,uint256 tokenId) external;
            
    // 授权NFT给另外一个地址 to
    function approve(address to, uint256 tokenId) external;
    // 持有的该系列NFT批量授权给某个地址operator
    function setApprovalForAll(address operator, bool _approved) external;
    // 查询tokenId被批准给了哪个地址。
    function getApproved(uint256 tokenId) external view returns (address operator);
    // 查询某地址的NFT是否批量授权给了另一个operator地址
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

4、IERC721Receiver

为了防止 NFT 转入黑洞合约,ERC721 实现了 safeTransferFrom() 安全转账函数,目标合约必须实现了 IERC721Receiver 接口才能接收ERC721代币,不然会 revert 。

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


interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

5、IERC721Metadata

IERC721Metadata 是 ERC721 的拓展接口,它定义了合约的元数据信息,包括合约名字、标志以及每个代币的 tokenURI。

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


interface IERC721Metadata {
    // 代币名称
    function name() external view returns (string memory);
    // 代币符号 
    function symbol() external view returns (string memory);
    // 通过 tokenId 查询 metadata 的链接url
    function tokenURI(uint256 tokenId) external view returns (string memory);
} 

6、ERC721

ERC-721 - 非同质化代币标准,主要用于发行独一无二的代币化资产如加密收藏品、游戏装备等。

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

import "./IERC165.sol";
import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./IERC721Metadata.sol";
import "./Address.sol";
import "./Strings.sol";

contract ERC721 is IERC721, IERC721Metadata {
    using Address for address; // 使用Address库,用isContract来判断地址是否为合约
    using Strings for uint256; // 使用String库,

    // Token名称  
    string public override name;
    // Token代号  
    string public override symbol;
    // tokenId 到 owner address 的持有人映射
    mapping(uint => address) private _owners;
    // address 到持仓数量的持仓量映射
    mapping(address => uint) private _balances;
    // tokenID 到授权地址的授权映射
    mapping(uint => address) private _tokenApprovals;
    //  owner地址。到operator地址的批量授权映射
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // 构造函数,初始化`name` 和`symbol` .
    constructor(string memory name_, string memory symbol_) {
        name = name_;
        symbol = symbol_;
    }

    // 实现IERC165接口 supportsInterface
    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
        return interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId;
    }

    // 实现IERC721的balanceOf,利用_balances变量查询owner地址的balance。
    function balanceOf(address owner) external view override returns (uint) {
        require(owner != address(0), "owner = zero address");
        return _balances[owner];
    }

    // 实现IERC721的ownerOf,利用_owners变量查询tokenId的owner。
    function ownerOf(uint tokenId) public view override returns (address owner) {
        owner = _owners[tokenId];
        require(owner != address(0), "token doesn't exist");
    }       

    // 实现IERC721的isApprovedForAll,利用_operatorApprovals变量查询owner地址是否将所持NFT批量授权给了operator地址。
    function isApprovedForAll(address owner, address operator) external view override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    // 实现IERC721的setApprovalForAll,将持有代币全部授权给operator地址。
    function setApprovalForAll(address operator, bool approved) external override {
        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    // 实现IERC721的getApproved,利用_tokenApprovals变量查询tokenId的授权地址。
    function getApproved(uint tokenId) external view override returns (address) {
        require(_owners[tokenId] != address(0), "token doesn't exist");
        return _tokenApprovals[tokenId];
    }

    // 授权函数。通过调整_tokenApprovals来,授权 to 地址操作 tokenId,同时释放Approval事件。
    function _approve(address owner, address to, uint tokenId) private {
        _tokenApprovals[tokenId] = to;
        emit Approval(owner, to, tokenId);
    }

    // 实现IERC721的approve,将tokenId授权给 to 地址。
    function approve(address to, uint tokenId) external override {
        address owner = _owners[tokenId];
        require(
            msg.sender == owner || _operatorApprovals[owner][msg.sender],
            "not owner nor approved for all"
        );
        _approve(owner, to, tokenId);
    }

    // 查询 spender地址是否可以使用tokenId(他是owner或被授权地址)。
    function _isApprovedOrOwner(address owner, address spender, uint tokenId) private view returns (bool) {
        return (spender == owner ||
            _tokenApprovals[tokenId] == spender ||
            _operatorApprovals[owner][spender]);
    }

    // 转账函数
    function _transfer( address owner, address from, address to, uint tokenId) private {
        require(from == owner, "not owner");
        require(to != address(0), "transfer to the zero address");

        _approve(owner, address(0), tokenId);   // 清空token的授权 
        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }
    
    // 实现IERC721的transferFrom,非安全转账,不建议使用。调用_transfer函数
    function transferFrom(address from, address to, uint tokenId) external override {
        address owner = ownerOf(tokenId);
        require(
            _isApprovedOrOwner(owner, msg.sender, tokenId),
            "not owner nor approved"
        );
        _transfer(owner, from, to, tokenId);
    }

    // 安全转账,安全地将 tokenId 代币从 from 转移到 to
    function _safeTransfer( address owner, address from, address to, uint tokenId, bytes memory _data ) private {
        _transfer(owner, from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "not ERC721Receiver");
    }

    // 实现IERC721的safeTransferFrom,安全转账,调用了_safeTransfer函数。
    function safeTransferFrom(address from, address to, uint tokenId, bytes memory _data) public override {
        address owner = ownerOf(tokenId);
        require(_isApprovedOrOwner(owner, msg.sender, tokenId), "not owner nor approved");
        _safeTransfer(owner, from, to, tokenId, _data);
    }

    // safeTransferFrom重载函数
    function safeTransferFrom( address from, address to, uint tokenId) external override {
        safeTransferFrom(from, to, tokenId, "");
    }

    // 铸造函数。通过调整_balances和_owners变量来铸造tokenId并转账给 to,同时释放Transfer事件。。
    function _mint(address to, uint tokenId) internal virtual {
        require(to != address(0), "mint to zero address");
        require(_owners[tokenId] == address(0), "token already minted");

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);
    }

    // 销毁函数,通过调整_balances和_owners变量来销毁tokenId,同时释放Transfer事件。
    function _burn(uint tokenId) internal virtual {
        address owner = ownerOf(tokenId);
        require(msg.sender == owner, "not owner of token");

        _approve(owner, address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);
    }

    // 调用IERC721Receiver-onERC721Received, 以防 tokenId 被不小心转入黑洞。
    function _checkOnERC721Received(address from, address to, uint tokenId, bytes memory _data) private returns (bool) {
        if (to.isContract()) {
            // 是否实现了 IERC721Receiver接口
            return IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data) 
                    == IERC721Receiver.onERC721Received.selector;
        } else {
            return true;
        }
    }

    // 实现IERC721Metadata的tokenURI函数,查询metadata。
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_owners[tokenId] != address(0), "Token Not Exist");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    // 计算{tokenURI}的BaseURI,tokenURI就是把baseURI和tokenId拼接在一起,需要开发重写。
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }
}

7、ERC721 NFT 的实现

再写一个合约继承 ERC721 , 并写好 MAX_APES 状态变量、构造函数、baseURI 函数以及锻造 mint 函数。

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

import "./ERC721.sol";

contract NFTSang is ERC721 {
    uint public MAX_APES = 10000; // 总量

    // 构造函数
    constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_){
    }

    // ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariAR3jxcaWtq/
    function _baseURI() internal pure override returns (string memory) {
        return "ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariAR3jxcaWtq/";
    }
    
    // 铸造函数
    function mint(address to, uint tokenId) external {
        require(tokenId >= 0 && tokenId < MAX_APES, "tokenId out of range");
        _mint(to, tokenId);
    }
}

8、编译部署

redmix 可以直接编译运行。

在这里插入图片描述

免费锻造代币,正式使用需要开发者继续完善,比如规定哪些人有锻造此代币的权限。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1280461.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【滑动窗口】水果成篮

水果成篮 904. 水果成篮 - 力扣&#xff08;LeetCode&#xff09; 文章目录 水果成篮题目描述问题转化 算法原理解法一解法二 代码编写C代码&#xff1a;使用容器数组模拟哈希表 Java代码使用容器数组模拟哈希表 题目描述 你正在探访一家农场&#xff0c;农场从左到右种植了一…

Java 数据结构篇-用链表、数组实现队列(数组实现:循环队列)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 队列的说明 1.1 队列的几种常用操作 2.0 使用链表实现队列说明 2.1 链表实现队列 2.2 链表实现队列 - 入栈操作 2.3 链表实现队列 - 出栈操作 2.4 链表实现队列 …

养身馆推拿会员管理系统,佳易王推拿会员管理软件短信设置教程

养身馆推拿会员管理系统&#xff0c;佳易王推拿会员管理软件短信设置教程 一、佳易王会员管理软件大众版 部分功能简介&#xff1a; 1、会员信息登记 &#xff1a;可以直接使用手机号登记&#xff0c;也可以使用实体卡片&#xff0c;推荐用手机号即可。 2、会员卡类型 &…

【每日一题】可获得的最大点数

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;滑动窗口方法二&#xff1a;前缀和 写在最后 Tag 【滑动窗口】【前缀和】【数组】【2023-12-03】 题目来源 1423. 可获得的最大点数 题目解读 在一排卡牌中拿出 k 张卡牌&#xff0c;每次必须从这一排卡牌的开头或者…

运维02:Linux

Linux安装 VMWare安装&#xff1a;夸克网盘分享&#xff08;提取码&#xff1a;refg&#xff09; CentOS安装&#xff1a;Index of /centos/7.9.2009/isos/x86_64/ Xshell安装&#xff1a;百度网盘 请输入提取码&#xff08;提取码&#xff1a;juau&#xff09; 环境准备 1、…

Pikachu(三)

RCE(remote command/code execute)概述 RCE漏洞&#xff0c;可以让攻击者直接向后台服务器远程注入操作系统命令或者代码&#xff0c;从而控制后台系统。 远程系统命令执行 一般出现这种漏洞&#xff0c;是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口 比如我…

Cmkae外部依赖管理

文章目录 一、cmake依赖管理介绍二、源码依管理1. FetchContent与find_package进行集成 2. CPM3. git submodule附加&#xff1a; address_sanitizer 和 undefined sanitizer 一、cmake依赖管理介绍 CMake 是跨平台的构建系统&#xff0c;支持 C/C、Objective-C、Fortran 等多种…

C++基础 -36- 模板之模板函数

模板函数格式 template <class T> void allexchange(T a,T b) {T c;c*a;*a*b;*bc; }模板函数可以增强函数的通用性 举例说明&#xff0c;使用一个模板函数实现了两个的函数的功能 #include "iostream"using namespace std;void myexchangeint(int* a,int* …

Grafana部署与Zabbix集成,搭建开源IT监控平台

Grafana部署与Zabbix集成 目前在一家公司主要是网络、运维、IT支持&#xff0c;每次需要检查服务器状态都是需要手动登录系统进行查看&#xff0c;因此想着部署一套监控系统&#xff0c;功能上需要实现监控、可视化、告警等。由于预算没有&#xff0c;服务器资源倒是有空闲的&a…

【hacker送书活动第7期】Python网络爬虫入门到实战

第7期图书推荐 内容简介作者简介大咖推荐图书目录概述参与方式 内容简介 本书介绍了Python3网络爬虫的常见技术。首先介绍了网页的基础知识&#xff0c;然后介绍了urllib、Requests请求库以及XPath、Beautiful Soup等解析库&#xff0c;接着介绍了selenium对动态网站的爬取和S…

电容和电感

一、电感 1&#xff09;图片 2&#xff09;作用 a&#xff09;储存容量 例如dcdc转换器的原理,将一个电压值转换成另外一个电压值 b&#xff09;选择信号 比如空气中弥漫着很多信号&#xff0c;我们应该怎么选取我们所需要的信号。 电感和电容可以看成一个电阻&#xff0c;当电…

Redis ziplist源码解析

area |<---- ziplist header ---->|<----------- entries ------------->|<-end->|size 4 bytes 4 bytes 2 bytes ? ? ? ? 1 byte--------------------------------------------------------------- comp…

MySQL进阶部分

存储引擎 MySQL体系结构图&#xff1a; 连接层&#xff1a; 最上层是一些客户端连接服务&#xff0c;主要完成一些类似于连接处理 &#xff0c;授权认证及相关的安全方案。服务器也会为安全接入的每个用户端验证它所具有的操作权限。 服务层&#xff1a; 第二层架构主要完成大…

数据科学:Matplotlib、Seaborn笔记

数据科学&#xff1a;Numpy、Pandas、Matplotlib、Seaborn 三、Matplotlib1.Matplotlib subplots函数2.tight_layout()函数3.Matplotlib grid()设置网格格式4.fill_between()函数示例设置x轴为时间刻度热力图 四、Seaborn1.set2.seaborn.scatterplot 参考 数据科学&#xff1a;…

github打不开,全网最简单解决方法,没有之一

下载watt toolkit&#xff0c; 选择‘github’&#xff0c;点击‘一键加速’&#xff0c; 具体步骤如下&#xff1a;去电脑微软商店下载watt toolkit&#xff0c;或者直接打开网址https://apps.microsoft.com/detail/9MTCFHS560NG?hlen-us&glUS 如图&#xff0c;点击安装i…

Sun Apr 16 00:00:00 CST 2023格式转换

Date date new Date(); log.info("当前时间为:{}",date); //yyyy-MM-dd HH:mm:ss SimpleDateFormat sdf new SimpleDateFormat(DateUtils.YYYY_MM_DD_HH_MM_SS); String dateTime s…

Android11适配已安装应用列表

Android11适配已安装应用列表 之前做过已安装应用列表的适配&#xff0c;最近国内版SDK升级到33和隐私合规遇到很多问题&#xff0c;于是把已安装应用列表记录一下&#xff1a; 1、在Android11及以上的适配&#xff1a; package com.example.requestinsttallapplistdemoimpo…

电磁兼容EMC理论基础汇总

目录 0. 序言 1. EMC的基础介绍 1.1 EMC电磁兼容的定义 1.2 EMC的重要性 1.3 EMC的三要素 2. 库仑定律 3. 趋肤效应与趋肤深度 4. 电阻抗公式 4.1 电阻 4.2 容抗 4.3 感抗 4.4 电路元件的非理想性 5. 麦克斯韦方程组 5.1 高斯磁定律 5.2 高斯定律 5.3 法拉…

一文讲透Python函数的创建和调用

1.Python提供了函数作为完成某项工作的标准化代码块 Python本质上是一种编程语言&#xff0c;通过编写运行代码的方式实现工作目标。读者可以想象&#xff0c;如果针对机器学习或数据统计分析的每种方法或统计量计算都要用户自行编写代码&#xff0c;那么显然在很多情况下是无…

Rust 语言:认识 Rust

本心、输入输出、结果 文章目录 Rust 语言&#xff1a;认识 Rust前言Rust的特点Rust LOGO Rust 在IT行业的应用前景Rust 是一门系统级编程语言相关链接花有重开日&#xff0c;人无再少年实践是检验真理的唯一标准 Rust 语言&#xff1a;认识 Rust 编辑&#xff1a;简简单单 Onl…