<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>fofo</title>
        <link>https://paragraph.com/@fofo</link>
        <description>undefined</description>
        <lastBuildDate>Sat, 22 Aug 2026 11:30:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>fofo</title>
            <url>https://storage.googleapis.com/papyrus_images/5ce5204f084965b5bf3e865146cf87da1c61600a8a39f6a6c39b9ceece851165.png</url>
            <link>https://paragraph.com/@fofo</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[一文带你了解NFT的底层技术有哪些]]></title>
            <link>https://paragraph.com/@fofo/nft</link>
            <guid>I8XxaOieC0q9TryNEA4Y</guid>
            <pubDate>Wed, 28 Sep 2022 13:54:34 GMT</pubDate>
            <description><![CDATA[一文带你了解NFT的底层技术有哪些 写在前面 不知道从什么时候起，NFT突然火了。你会时不时的看到一个新闻说某个数字藏品的NFT买了几十万美金，似乎NFT是财富密码一样。 如果你关注这个行业，你肯定挺听过无聊猿游艇俱乐部（BAYC），BAYC在opensea上有大概10000只NFT，图像是各种各样的猿。比如下面这种 关于NFT的各种咨询的文章到处都是，不过涉及底层技术的却比较少，本文就带你了解下是哪些技术支撑着这个全球交易规模数百亿美元的产业。（根据第三方数据机构 nonfungible 统计，2021 年 NFT 交易规模达到 140 亿美元） 不过要强调的是，我不会就某个技术细节详细展开，比如讲到区块链，不会详细到给你讲共识算法，这篇文章只是抛砖引玉，有兴趣的可以就某个知识点自行查阅。 相关的底层技术 NFT是 Non-Fungible Token 的缩写，意为非同质化通证（或代币）。这里有个概念叫非同质化，说白了就是不一样的意思。我们平时使用的货币，比如人民币和美元，或者是一些数字货币比如BTC等都是同质化的，他们可以交换，可以被替换，因为我们只关心货币的面值，而不是钞票...]]></description>
            <content:encoded><![CDATA[<p>一文带你了解NFT的底层技术有哪些</p><p>写在前面 不知道从什么时候起，NFT突然火了。你会时不时的看到一个新闻说某个数字藏品的NFT买了几十万美金，似乎NFT是财富密码一样。</p><p>如果你关注这个行业，你肯定挺听过无聊猿游艇俱乐部（BAYC），BAYC在opensea上有大概10000只NFT，图像是各种各样的猿。比如下面这种</p><p>关于NFT的各种咨询的文章到处都是，不过涉及底层技术的却比较少，本文就带你了解下是哪些技术支撑着这个全球交易规模数百亿美元的产业。（根据第三方数据机构 nonfungible 统计，2021 年 NFT 交易规模达到 140 亿美元）</p><p>不过要强调的是，我不会就某个技术细节详细展开，比如讲到区块链，不会详细到给你讲共识算法，这篇文章只是抛砖引玉，有兴趣的可以就某个知识点自行查阅。</p><p>相关的底层技术 NFT是 Non-Fungible Token 的缩写，意为非同质化通证（或代币）。这里有个概念叫非同质化，说白了就是不一样的意思。我们平时使用的货币，比如人民币和美元，或者是一些数字货币比如BTC等都是同质化的，他们可以交换，可以被替换，因为我们只关心货币的面值，而不是钞票上的唯一编码。</p><p>NFT的非同质化，意思是说每个NFT都是独一无二的，被加密的，不可替换的。而且还有一个最大的特点是不可分割。它的特点有点像我们平时生活中的房产证、合同，票据之类的东西。</p><p>NFT在区块链技术栈的位置大概是这样的：</p><p>最底层当然还是区块链，这一层主要是提供基础设施，比如共识算法，P2P网络等。这个链可以是公链，也可以是联盟链，甚至是私链。</p><p>公链领域当然首先要提以太坊，以太坊是NFT最早的公链。但是由于后来以太坊主网的拥堵，后来又出现了一些别的公链，比如flow，SOLANA等。关于以太坊拥堵的前后背景，如果想了解可以看下面这篇文章：</p><p>白话ETH2.0：你要了解的都在这里</p><p>像我们前面提到的BAYC，就是在以太坊主网发行的NFT。</p><p>区块链本身是一个去中心化的分布式账本，其使用的哈希加密算法是具有原像抗性和次原像抗性，我们在区块链上发行的NFT本身就是在链上确认的交易，那么交易一旦确认形成区块加入主链，那这个NFT就是不能被恶意篡改的，它具有唯一的标识。</p><p>再往上看，是虚拟机EVM，EVM（ETHereum Virtual Machine）是「以太坊虚拟机」的缩写，EVM的存在是为了能让我们编写的智能合约代码，能够被解析并运行在公链环境中。关于EVM里面的一些细节，可以看我以前写的一篇文章：</p><p>以太坊虚拟机EVM究竟是个啥</p><p>下一个要说的技术点是智能合约。基于区块链的智能合约使用图灵完备的脚本语言来实现复杂功能的兼容，并通过依靠共识算法来进行执行，以保证一致性。智能合约让不依赖第三方信用中介的公平交易成为可能，可以实现跨行业、跨领域、跨生态的价值交互。</p><p>NFT的本质其实是由智能合约创建、维护、执行的非同质化数字资产通证。要创建NFT，首先你要有一个智能合约，这个智能合约在以太坊主链上会有一个唯一的地址，比如前面提到的BAYC，它智能合约的地址是</p><p>0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D 1 我们可以在以太坊的浏览器中查看这个地址：</p><p>然后基于这个合约，我们可以创建NFTs，这里加s表示可以创建多个不同的NFT，比如BAYC就有10000个不同的猿，每个猿的区别是他们的元数据（metadata）不同，所以才会展示不同的图像效果。</p><p>有了智能合约后，如果要创建NFT，通常我们遵循某个协议，现自常用的协议是ERC721，ERC1155和ERC998三种。使用不同的协议创建NFT，一般是出于不同的使用场景，比如ERC1155的交易速度比ERC721高等，我们只需要关注其中一种协议创建nft的方法，其他的也不难。</p><p>所以这里我就以ERC721为例，简单说明下创建NFT的过程。下面的内容会展示小部分代码，这些代码的语法是solidity，这是在以太坊编写智能合约的一个语言。不会这个语言也没关系，大概能看懂什么意思就可以了。</p><p>ERC721协议定义了一组接口方法和事件，你写一个智能合约只要实现了这些方法和事件，它就是一个NFT的智能合约。</p><p>方法，</p><pre data-type="codeBlock" text="function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">balanceOf</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _owner</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ownerOf</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">safeTransferFrom</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _from, <span class="hljs-keyword">address</span> _to, <span class="hljs-keyword">uint256</span> _tokenId, <span class="hljs-keyword">bytes</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">safeTransferFrom</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _from, <span class="hljs-keyword">address</span> _to, <span class="hljs-keyword">uint256</span> _tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transferFrom</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _from, <span class="hljs-keyword">address</span> _to, <span class="hljs-keyword">uint256</span> _tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approve</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _approved, <span class="hljs-keyword">uint256</span> _tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setApprovalForAll</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _operator, <span class="hljs-keyword">bool</span> _approved</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getApproved</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isApprovedForAll</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _owner, <span class="hljs-keyword">address</span> _operator</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;
</code></pre><p>1 2 3 4 5 6 7 8 9 10 随便挑几个方法解释下，</p><p>ownerOf函数，参数是_tokenId，它返回该token的持有者地址</p><p>approve函数，可以授权给另外一个人转移代币的权利</p><p>事件，</p><pre data-type="codeBlock" text="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);
"><code><span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Transfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> _from, <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> _to, <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> _tokenId</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Approval</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> _owner, <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> _approved, <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> _tokenId</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">ApprovalForAll</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> _owner, <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> _operator, <span class="hljs-keyword">bool</span> _approved</span>)</span>;
</code></pre><p>1 2 3 4 上面这些是标准接口，还有一些扩展接口，根据需要决定是否实现。比如还有 ERC721Metadata 接口，这个是元数据接口，前面我们也提到了元数据。</p><p>/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension /// @dev See <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-721">https://eips.ethereum.org/EIPS/eip-721</a> /// Note: the ERC-165 identifier for this interface is 0x5b5e139f. interface ERC721Metadata /* is ERC721 */ { /// @notice A descriptive name for a collection of NFTs in this contract function name() external view returns (string _name);</p><pre data-type="codeBlock" text="/// @notice An abbreviated name for NFTs in this contract
function symbol() external view returns (string _symbol);

/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
///  3986. The URI may point to a JSON file that conforms to the &quot;ERC721
///  Metadata JSON Schema&quot;.
function tokenURI(uint256 _tokenId) external view returns (string);
"><code><span class="hljs-comment">/// @notice An abbreviated name for NFTs in this contract</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">symbol</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span> _symbol</span>)</span>;

<span class="hljs-comment">/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.</span>
<span class="hljs-comment">/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC</span>
<span class="hljs-comment">///  3986. The URI may point to a JSON file that conforms to the "ERC721</span>
<span class="hljs-comment">///  Metadata JSON Schema".</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tokenURI</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span></span>)</span>;
</code></pre><p>} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 还有枚举相关的接口，</p><p>/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension /// @dev See <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-721">https://eips.ethereum.org/EIPS/eip-721</a> /// Note: the ERC-165 identifier for this interface is 0x780e9d63. interface ERC721Enumerable /* is ERC721 */ { /// @notice Count NFTs tracked by this contract /// @return A count of valid NFTs tracked by this contract, where each one of /// them has an assigned and queryable owner not equal to the zero address function totalSupply() external view returns (uint256);</p><pre data-type="codeBlock" text="/// @notice Enumerate valid NFTs
/// @dev Throws if `_index` &gt;= `totalSupply()`.
/// @param _index A counter less than `totalSupply()`
/// @return The token identifier for the `_index`th NFT,
///  (sort order not specified)
function tokenByIndex(uint256 _index) external view returns (uint256);

/// @notice Enumerate NFTs assigned to an owner
/// @dev Throws if `_index` &gt;= `balanceOf(_owner)` or if
///  `_owner` is the zero address, representing invalid NFTs.
/// @param _owner An address where we are interested in NFTs owned by them
/// @param _index A counter less than `balanceOf(_owner)`
/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
///   (sort order not specified)
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
"><code><span class="hljs-comment">/// @notice Enumerate valid NFTs</span>
<span class="hljs-comment">/// @dev Throws if `_index` >= `totalSupply()`.</span>
<span class="hljs-comment">/// @param _index A counter less than `totalSupply()`</span>
<span class="hljs-comment">/// @return The token identifier for the `_index`th NFT,</span>
<span class="hljs-comment">///  (sort order not specified)</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tokenByIndex</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _index</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;

<span class="hljs-comment">/// @notice Enumerate NFTs assigned to an owner</span>
<span class="hljs-comment">/// @dev Throws if `_index` >= `balanceOf(_owner)` or if</span>
<span class="hljs-comment">///  `_owner` is the zero address, representing invalid NFTs.</span>
<span class="hljs-comment">/// @param _owner An address where we are interested in NFTs owned by them</span>
<span class="hljs-comment">/// @param _index A counter less than `balanceOf(_owner)`</span>
<span class="hljs-comment">/// @return The token identifier for the `_index`th NFT assigned to `_owner`,</span>
<span class="hljs-comment">///   (sort order not specified)</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tokenOfOwnerByIndex</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _owner, <span class="hljs-keyword">uint256</span> _index</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;
</code></pre><p>} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 下面这个链接，你可以看到一个实现了ERC721的例子：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol">https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol</a></p><p>这里例子属于OpenZeppelin的一部分，Openzeppelin是一个开源的智能合约库，我们可以很方便的通过直接继承它里面的一些实现好的类来写一个自己的NFT，比如：</p><p>pragma solidity ^0.8.0;</p><p>import &quot;@openzeppelin/contracts/token/ERC721/ERC721.sol&quot;; import &quot;@openzeppelin/contracts/utils/Counters.sol&quot;; import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;; import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol&quot;;</p><p>contract MyNFT is ERC721URIStorage, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIds;</p><pre data-type="codeBlock" text="   constructor() ERC721(&quot;MyNFT&quot;, &quot;NFT&quot;) {}

   function mintNFT(address recipient, string memory tokenURI)
       public onlyOwner
       returns (uint256)
   {
       _tokenIds.increment();

       uint256 newItemId = _tokenIds.current();
       _mint(recipient, newItemId);
       _setTokenURI(newItemId, tokenURI);

       return newItemId;
   }
"><code>   <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC721</span>(<span class="hljs-params"><span class="hljs-string">"MyNFT"</span>, <span class="hljs-string">"NFT"</span></span>) </span>{}

   <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintNFT</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> recipient, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> tokenURI</span>)
       <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span>
       <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)
   </span>{
       _tokenIds.increment();

       <span class="hljs-keyword">uint256</span> newItemId <span class="hljs-operator">=</span> _tokenIds.current();
       _mint(recipient, newItemId);
       _setTokenURI(newItemId, tokenURI);

       <span class="hljs-keyword">return</span> newItemId;
   }
</code></pre><p>} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 这个代码很短，它包含一个计数器(Counter)、一个构造函数(constructor)，和一个函数（mintNFT）。是的，这些代码我们就实现了一个简单的NFT。这要得益于ERC721URIStorage，这个的OpenZeppelin合约类，它已经帮我们实现了前面提到的协议接口中的大部分方法。</p><p>总结 目前全球NFT的主要应用还是在数字藏品领域，前面提到的BAYC就属于这个领域。我个人觉得这个领域泡沫比较严重，存在大量炒作投机的行为。未来NFT想要得到快速的发展还是需要落地一些真正解决社会问题的场景，比如一些工具类的应用，个人信息的认证等。否则，最终的命运很可能像当年的ico一样昙花一现。</p><p>参考：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum.org/en/developers/docs/standards/tokens/erc-721/">https://ethereum.org/en/developers/docs/standards/tokens/erc-721/</a> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-721">https://eips.ethereum.org/EIPS/eip-721</a> ————————————————</p>]]></content:encoded>
            <author>fofo@newsletter.paragraph.com (fofo)</author>
        </item>
    </channel>
</rss>