赞
踩
ERC-20是同质化代币标准,同质化的意思就是说,代币在单位内表示的价值和功能是一样, 代币之间是可以互换的。比如你现在有100个BNB, 那么每一个BNB价值和功能都是一样的。
但是,有时候我们希望给区块链中的虚拟资产赋予独特的、唯一的等特性的时候,比如对数字资产的所有权、纪念章、数字艺术品、各种唯一的虚拟物品,比如游戏道具等等,这时候同质化代币是做不到这一点,因此有了非同质化代币NFT,即Non-Fungible Tokens
NFT: 就是Non-Fungible Token,即非同质化代币,他也是以太坊区块链上一种虚拟数字资产,但是和同质化代币不同,它是唯一的,独特的,不可互换的代币,每一个NFT都具有独特的属性和价值。
Token是可以互换的,每一个Token的价值和功能是一样的
NFT: 不可互换,每一个NFT都是独一无二的,都可能具有不同价值
Token是可以分割的,通常可以分割为更小的单位进行交易。比如你可以购买0.1个或者0.001个代币
NFT通常不可分割,每个NFT都是一个完整的单位,你不能购买0.5个NFT
Token主要用于支付、交易和价值存储,适合需要标准化价值的场景
NFT主要用于表示独特的资产和所有权,适合需要强调独特性和所有权的场景。比如游戏道具、数字艺术品、数字收藏品、所有权、数字票据等等
ERC-721是以太坊非同质化代币(NFT)的一个标准,它定义了一套规范或者标准,如果NFT实现了ERC-721标准,外部的钱包、交易所或者其他DApp应用程序就可以很方便的和NFT进行交互。
非同质化:
与同质化代币(如比特币和以太币)不同,ERC-721 代币是独特的,每个代币有其独特的属性和标识符。
这种独特性使得 ERC-721 代币特别适合表示独一无二的资产,如数字艺术品、收藏品、游戏物品等。
不可分割:
大多数 ERC-721 代币是不可分割的,即不能像同质化代币那样分割成更小的单位。一个完整的 ERC-721 代币必须作为一个整体进行交易。
所有权和转移:
ERC-721 标准定义了一组接口,使得NFT的所有权和转移过程可以在区块链上透明和安全地进行。代币的所有权信息被永久记录在区块链上,任何人都可以验证。
和ERC-20不同,ERC-721因为要表示代币独一无二。因此,给NFT添加了一个tokenId的字段,表示该NFT独一无二。
另外,每一个NFT都有一个tokenURI,通常指向一个包含该代币详细信息的 JSON 文件,如名称、描述、图像链接等。这使得每个 NFT 可以有丰富的元数据,与其独特性相对应。比如:
{
"title": "Louis Vena",
"description": "Cneter Left",
"image": "ipfs://QmX6pUBZDRt2uzw79iatA9zH9sbbDrrT1wThs8cjamqAkF",
"attributes": [
{
"trait_type": "Singer",
"value": "Belly"
},
{
"trait_type": "Area",
"value": "America"
}
],
"version": "1"
}
function balanceOf(address _owner) external view returns (uint256);
返回指定地址的NFT余额,一个人可能会拥有多个
2.3.2.2 ownerOf
function ownerOf(uint256 _tokenId) external view returns (address);
根据指定的tokenId返回该NFT的拥有者地址
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
安全的将指定的tokenId的NFT从_from地址转移到_to地址,并且还可以携带一些额外数据。
在复杂的交易场景中,可能需要附加的上下文信息。例如,某个 NFT 可能代表一个游戏中的物品,附加的数据可以包含关于游戏场景的信息。
在目标地址是合约时,附加的数据可以用来传递特定指令给合约。
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
安全的将指定的tokenId的NFT从_from地址转移到_to地址,不能携带一些额外数据
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
将指定的tokenId的NFT从_from地址转移到_to地址
function approve(address _approved, uint256 _tokenId) external payable;
授权某个地址_approved可以转移指定tokenId的NFT。
function setApprovalForAll(address _operator, bool _approved) external;
授权或取消授权_operator地址可以管理调用者的所有NFT
function getApproved(uint256 _tokenId) external view returns (address);
返回指定tokenId的NFT被授权哪个地址可以进行转移
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
是否_operator是否被授权管理_owner的所有NFT
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);
在设置或取消所有授权时触发
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
都是将 指定tokenId的NFT 从 `from` 地址转移到` to` 地址
不同点主要是安全问题:
我们知道智能合约地址一般分为EOA地址和CA地址,那么钱包一般属于EOA地址,这类地址他们不会有代码的执行,仅用于持有和发送代币等;但是CA地址就是合约地址,合约地址是可以有代码执行逻辑的。
那这个和NFT转移安全有什么关系呢? 难道CA地址就无法转移成功,EOA地址就可以?
其实是这样的,无论是EOA地址还是CA地址,safeTransferFrom和transferFrom都是可以转移成功的。但是转移给CA地址,需要考虑到这个合约是否有接口可以把NFT转出来,如果转不出来,那么这个NFT就一直在这个合约里面了。因此,如果 to 地址是一个不支持 操作ERC721的合约,最后safeTransferFrom就会revert, 这笔交易不会成功。
那safeTransferFrom是怎么实现检查合约是否具备操作NFT的能力的呢?
在OpenZeppelin中,定义了一个ERC721Receiver接口,如果你的合约你觉得需要让调用者知晓你是具备操作ERC721的能力的,比如转账,那么你需要在你的合约中实现这个ERC721Receiver接口的onERC721Received函数;否则调用者就无法知晓你这个合约到底是不是支持ERC721接口的,那么safeTransferFrom就会认为不支持,则判定失败,回滚交易。
- function onERC721Received(
- address operator,
- address from,
- uint256 tokenId,
- bytes calldata data
- ) external returns (bytes4);
那么实现了或者重载了,怎么认为他就具备处理ERC721的能力了?只需要这个函数返回值是`bytes4`类型,并且返回值为`this.onERC721Received.selector`,这样可以确保函数符合ERC721接口中定义的规范。比如:
- function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external override returns (bytes4) {
-
- // 这里可以添加处理逻辑,例如记录接收到的代币信息
-
- // 返回这个值表明成功接收
-
- return this.onERC721Received.selector;
-
- }
- // SPDX-License-Identifier: UNLICENSED
-
- pragma solidity ^0.8.13;
-
- import "./IERC721.sol";
-
-
-
- abstract contract ERC720 is IERC721 {
-
- string private _name;
-
- string private _symbol;
-
-
-
- mapping(uint256 => address) private _owners;
-
- mapping(address => uint256) private _balances;
-
- mapping(uint256 => address) private _approvals;
-
- mapping(address owner => mapping(address operator => bool))
-
- private _operatorApprovals;
-
-
-
- constructor(string memory name, string memory symbol) {
-
- _name = name;
-
- _symbol = symbol;
-
- }
-
-
-
- function name() public view virtual returns (string memory) {
-
- return _name;
-
- }
-
-
-
- function symbol() public view virtual returns (string memory) {
-
- return _symbol;
-
- }
-
-
-
- function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
-
- require(_owners[tokenId] != address(0), "Not exists owner");
-
-
-
- string memory baseURI = _baseURI();
-
- return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
-
- }
-
-
-
- // Any contract extends the ERC721 contract could ovveride the function and set the baseURI value, default is empty string
-
- function _baseURI() internal view virtual returns (string memory) {
-
- return "";
-
- }
-
-
-
- function balanceOf(
- address owner
- ) external view override returns (uint256 balance) {
-
- return _balances[owner];
-
- }
-
-
-
- function ownerOf(
- uint256 tokenId
- ) external view override returns (address owner) {
-
- return _owners[tokenId];
-
- }
-
-
-
- function safeTransferFrom(
- address from,
- address to,
- uint256 tokenId,
- bytes calldata data
- ) external override {
-
- _checkOnERC721Received(from, to, tokenId, data);
-
- transferFrom(from, to, tokenId);
-
- }
-
-
-
- function safeTransferFrom(
- address from,
- address to,
- uint256 tokenId
- ) external override {
-
- safeTransferFrom(from, to, tokenId, "");
-
- }
-
-
-
- /**
- * Transfer ownership of an NFT
- * @param from
- * @param to
- * @param tokenId
- */
-
- function transferFrom(
- address from,
- address to,
- uint256 tokenId
- ) external override {
-
- // check if msg.sender has approved
-
- require(_checkPermission(from, msg.sender, tokenId), "msg.sender has no previleges to transfer");
-
- // update owner balance
-
- address prev = _update(to, tokenId, msg.sender);
-
- require(prev == from, "Incorrect owner");
-
- }
-
-
-
- // Approve permisson to `to` address for tokenId
-
- function approve(address to, uint256 tokenId) external override {
-
- _approve(to, tokenId, msg.sender);
-
- }
-
-
-
- function setApprovalForAll(
- address operator,
- bool approved
- ) external override {
-
- require(operator != address(0), "Invalid operator");
-
- _operatorApprovals[msg.sender][operator] = approved;
-
- emit ApprovalForAll(msg.sender, operator, approved);
-
- }
-
-
-
- function getApproved(
- uint256 tokenId
- ) external view override returns (address operator) {
-
- require(_owners[tokenId] != address(0), "Not exist owner");
-
- return _getApproved(tokenId);
-
- }
-
-
-
- function isApprovedForAll(
- address owner,
- address operator
- ) public view override returns (bool) {
-
- return _operatorApprovals[owner][operator];
-
- }
-
-
-
- function _update(address to, uint256 tokenId, address operator) internal virtual returns (address) {
-
- address from = _owners[tokenId];
-
-
-
- if (operator != address(0)) {
-
- _checkPermission(from, operator, tokenId);
-
- }
-
-
-
- if (from != address(0)) {
-
- unchecked {
-
- _balances[from] -= 1;
-
- }
-
- }
-
-
-
- if (to != address(0)) {
-
- unchecked {
-
- _balances[to] += 1;
-
- }
-
- }
-
-
-
- _owners[tokenId] = to;
-
-
-
- emit Transfer(from, to, tokenId);
-
-
-
- return from;
-
- }
-
-
-
- function _approve(address to, uint256 tokenId, address owner) internal virtual {
-
- require(_owners[tokenId] == owner, "Incorrect owner address")
-
- require(to != address(0), "Invalid to address");
-
- _approvals[tokenId] = to;
-
- emit Approval(owner, to, tokenId);
-
- }
-
-
-
- function _getApproved(uint256 tokenId) internal view virtual returns (address) {
-
- return approvals[tokenId];
-
- }
-
-
-
- /*
- * Check if owner approved for spender about the tokenId
- * @param owner
- * @param spender
- * @param tokenId
- */
-
- function _checkPermission(address owner, address operator, uint256 tokenId) internal returns (bool) {
-
- require(_owners[tokenId] == owner, "Incorrect owner");
-
- require(operator != address(0), "Illegal operator address");
-
- // If operator is owner, so operator must have the permission
-
- if (owner == operator) {
-
- return true;
-
- }
-
-
-
- // If operator is approved, return true
-
- if (_getApproved(tokenId) == operator) {
-
- return true;
-
- }
-
-
-
- return isApprovedForAll(owner, operator);
-
- }
-
-
-
- function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) internal {
-
- // check smart contract code length, EOA address length is zero, CA address length is greater than zero
-
- if (to.code.length > 0) {
-
- // if to address is contract address, see if implements the IERC721Receiver interface
-
- // if IERC721Receiver.onERC721Received return value is IERC721Receiver.onERC721Received.selector
-
- // it means the contract could operate NFT, such as transfer .etc
-
- try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
-
- if (retval != IERC721Receiver.onERC721Received.selector) {
-
- revert ERC721InvalidReceiver(to);
-
- }
-
- } catch (bytes memory reason) {
-
- if (reason.length == 0) {
-
- revert ERC721InvalidReceiver(to);
-
- } else {
-
- assembly {
-
- revert(add(32, reason), mload(reason))
-
- }
-
- }
-
- }
-
- }
-
- }
-
-
-
- /**
- * Get nft approver address by tokenId
- */
-
- function _getApproved(uint256 tokenId) internal view virtual returns (address) {
-
- return _approvals[tokenId];
-
- }
-
-
-
- /**
- * Mint nft to `to` address
- * @param to
- * @param tokenId
- */
-
- function _mint(address to, uint256 tokenId) internal {
-
- require(to != address(0), "Invalid mint address");
-
- unchecked {
-
- _balances[to] += 1;
-
- }
-
- _owners[tokenId] = to;
-
-
-
- emit Transfer(from, to, tokenId);
-
- }
-
-
-
- function _safeMint(address to, uint256 tokenId) internal {
-
- _safeMint(to, tokenId, "");
-
- }
-
-
-
- function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
-
- _mint(to, tokenId);
-
- _checkOnERC721Received(address(0), to, tokenId, data);
-
- }
-
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。