<?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>Monkey</title>
        <link>https://paragraph.com/@monkey-11</link>
        <description>undefined</description>
        <lastBuildDate>Tue, 08 Sep 2026 10:23: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>Monkey</title>
            <url>https://storage.googleapis.com/papyrus_images/386da93f850f5ab618d19573532e27797c86030b129b1090aa4a2b3cfbd22c00.png</url>
            <link>https://paragraph.com/@monkey-11</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Alchemy第一周边学边获取NFT]]></title>
            <link>https://paragraph.com/@monkey-11/alchemy-nft-2</link>
            <guid>lzgyc5bDRQcLQli5pErO</guid>
            <pubDate>Fri, 14 Oct 2022 08:25:12 GMT</pubDate>
            <description><![CDATA[Alchemy计划将新资金用于推广Web3采用，这方面的一些举措包括推出Web3 University，就是现在的 Road to Web3 活动，活动为期10周，每周一个NFT。 该课程是为期10周，只要你在10周完成就可以。 今天我们先来说下第一个任务课程：如何使用 Alchemy 开发 NFT 智能合约（ERC721） 官方提供了一个本课程的大纲：如何使用 OpenZeppelin 和 Remix 编写和修改智能合约使用Goerl网络获得免费的 Rinkeby ETH将其部署在以太坊 Goerl测试网区块链上以节省汽油费使用 Filebase 在 IPFS 上托管 NFT 令牌元数据。铸造 NFT 并在 OpenSea 上可视化1.编写合约 首先我们来到合约编写页面合约编写页面合约编写页面 2.将合约使用Remix修改和部署导入Remix后合约的结构如下：我们需要修改如下几点： 1.该NFT是任何人都可以mint的，所以修改如下 function safeMint(address to, string memory uri) public { require(_tokenI...]]></description>
            <content:encoded><![CDATA[<p>Alchemy计划将新资金用于推广Web3采用，这方面的一些举措包括推出Web3 University，就是现在的 Road to Web3 活动，活动为期10周，每周一个NFT。</p><p>该课程是为期10周，只要你在10周完成就可以。</p><p>今天我们先来说下第一个任务课程：如何使用 Alchemy 开发 NFT 智能合约（ERC721）</p><p>官方提供了一个本课程的大纲：</p><ul><li><p>如何使用 OpenZeppelin 和 Remix 编写和修改智能合约</p></li><li><p><strong>使用</strong>Goerl网络获得免费的 Rinkeby ETH</p></li><li><p>将其部署在以太坊 Goerl测试网区块链上以节省汽油费</p></li><li><p>使用 Filebase 在 IPFS 上托管 NFT 令牌元数据。</p></li><li><p>铸造 NFT 并在 OpenSea 上可视化</p></li></ul><p><strong>1.编写合约</strong></p><p>首先我们来到合约编写页面</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a4b7de7e1ade2eac51601e0d81f05a4e061f0193a0da1a4ec7aacd55a07c22bc.png" alt="合约编写页面" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">合约编写页面</figcaption></figure><p>合约编写页面</p><p><strong>2.将合约使用Remix修改和部署</strong></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/98f7211d81c080427427c7c54eb709e84bf86ef47a1394a1d3b33c70983ac4f5.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>导入Remix后合约的结构如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9471821557118d96c2098d6e8bfb680c7c03b4a0fb6d64fc39d16d2bdfa0ee55.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>我们需要修改如下几点：</p><p>1.该NFT是任何人都可以mint的，所以修改如下</p><pre data-type="codeBlock" text="  function safeMint(address to, string memory uri) public {
        require(_tokenIdCounter.current() &lt;= MAX_SUPPLY, &quot;I&apos;m sorry we reached the cap&quot;);
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }
"><code>  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">safeMint</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> uri</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(_tokenIdCounter.current() <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> MAX_SUPPLY, <span class="hljs-string">"I'm sorry we reached the cap"</span>);
        <span class="hljs-keyword">uint256</span> tokenId <span class="hljs-operator">=</span> _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }
</code></pre><p>2.修改NFT的供应总量</p><pre data-type="codeBlock" text=" uint256 MAX_SUPPLY = 100000; //将该代码添加在Counters.Counter private _tokenIdCounter下一行
"><code> uint256 <span class="hljs-attr">MAX_SUPPLY</span> = <span class="hljs-number">100000</span><span class="hljs-comment">; //将该代码添加在Counters.Counter private _tokenIdCounter下一行</span>
</code></pre><p><strong>3.获取测试token</strong></p><p>我们去到下面的网站获取测试Token，同时需要大家去Alchemy注册一个账号</p><p><strong>4.创建一个Alchemy的app</strong></p><p>1.首先我们去到Alchemy注册一个账户，同时新建一个app（需要选择以太坊生态和Rikney）</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/aaaad2a0e0ab853c4f37ed07331d8aae88e1e2fdc97e2687887d6bf0e7a20569.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>查看我们的Key</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c0810393bfe17378eb7a51d0255c6454120290bd90ea1d305e9f122352db45f3.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>5.将 Alchemy Rinkeby 添加到 Metamask 钱包，信息如下</strong></p><ul><li><p><strong>Network name:</strong> Alchemy Rinkeby</p></li><li><p><strong>New RPC URL:</strong> 你申请的app的地址，去上面的viewkey获取即可</p></li><li><p><strong>Chain ID:</strong> 4</p></li><li><p><strong>Currency Symbol:</strong> ETH</p></li><li><p><strong>Block Explorer:</strong> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://rinkeby.etherscan.io/">https://rinkeby.etherscan.io/</a></p></li></ul><p><strong>6.部署发布我们的合约</strong></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/62c4c9c043d64cbf3a0220be3d69ae8e83818df634318ec1c8cebdbbb697125b.png" alt="部署合约" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">部署合约</figcaption></figure><p>部署合约</p><p>将出现一个 Metamask 弹出窗口，点击“签名”，然后继续支付 gas 费用。如果一切都按预期工作，10 秒后，应该会在 Deployed Contracts 下看到该合约：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d1c6e273095c3c038d4842b110d6ef64573f16a8db0b13d32b1efb747809bb85.png" alt="已经部署完成的合约" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">已经部署完成的合约</figcaption></figure><p>已经部署完成的合约</p><p><strong>7.上传我们的NFT元数据</strong></p><p>Pinata网站可以管理我们的元数据，一定要去学会怎么使用，我们只需要申请一个账号，会免费给1g的存储空间。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b5c6a3728e3a7c34760c07395af0f6abfdc603de62f771d6547975ef3fe8bab3.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>mint我们的NFT，在safeMint输入我们的钱包地址和刚才编写的uri（ipfs://\&lt;your\_metadata\_cid&gt;）使用json的。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1d3b75264a4b108c194cd71b0a6adc736d0931eb6e9616336e557ba78ee88206.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>8.查看我们的NFT</strong></p><p>我们去到OpenSea测试网站查看我们做的NFT</p><p>我们找到对应合约的NFT，现在我们看到的合约地址和我们刚才部署的合约地址是否对应上，现在还是盲盒，需要我们自己去操作下</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/12a2ab897f7fc0dda3dbefa777be8541a6a9428ab049510db11b8af534fa3ae7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>9.开盲盒</strong></p><p>在tokenUri 插入“0”作为 id 参数，点击call，它应该显示你的 tokenURI。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/72f03fdf46c3ab34f87dcccd61c98090e9c70c5b5555ca2881ed041dfa7a6e79.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/13e3883c06de07b07667a48633ee816f63ba955b698c4dc2bb9eb5283e99635c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>需要等待一段时间，大家可以先试试。</p>]]></content:encoded>
            <author>monkey-11@newsletter.paragraph.com (Monkey)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy - 第四周NFT获取教程]]></title>
            <link>https://paragraph.com/@monkey-11/alchemy-nft</link>
            <guid>MkDknl9iY6ulcdbVytbQ</guid>
            <pubDate>Fri, 14 Oct 2022 08:13:20 GMT</pubDate>
            <description><![CDATA[step1 创建项目设置1.打开控制台并输入以下代码，从而创建 Next JS 项目样板并安装 TailwindCSS。 npx create-next-app -e with-tailwindcss nameoftheproject ，如图。2.输入cd nameoftheproject && code . （最后那个点别掉了，掉了打开的就是之前的项目）3.在控制台输入npm run dev ，如果电脑弹出防火墙，就点允许访问，不弹出也没关系。4.回到vscode，将index.tsx和_app.tsx的后缀改成.jsx，删除_app.jsx中报错的部分，如图。 或者直接粘贴下面的代码替换。import '../styles/globals.css' function MyApp({ Component, pageProps }) { return &#x3C;Component {...pageProps} /> } export default MyApp step2 修改index.jsx代码1.这一步具体每行代码的意义可以到官方看，我这里放最终的代码，方便大家完成任务，...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1c054dd55f6a8d753168765883588ce050b4ba11657a492dbd2ad1723a098396.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-step1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step1 创建项目设置</h3><p>1.打开控制台并输入以下代码，从而创建 Next JS 项目样板并安装 TailwindCSS。</p><p>npx create-next-app -e with-tailwindcss nameoftheproject ，如图。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/623c5cb219320437fa0c4b726b2dbc1eb2434dd9763b4e69968852ae491fec35.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>2.输入cd nameoftheproject &amp;&amp; code . （最后那个点别掉了，掉了打开的就是之前的项目）</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d75914894ef20e02902479c4beeb12663f7ddc2bab7104043402ef964aa39aa7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>3.在控制台输入npm run dev ，如果电脑弹出防火墙，就点允许访问，不弹出也没关系。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8bf186a6794f900393fd3f9df465489d26213fe4e6fe7c0bc7ff2975aecea0e0.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>4.回到vscode，将index.tsx和_app.tsx的后缀改成.jsx，删除_app.jsx中报错的部分，如图。</p><p>或者直接粘贴下面的代码替换。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c9051fa275d9051dc8effb67f631b7b2b75c6cc87096eb44a572cb5b094648b9.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><pre data-type="codeBlock" text="import &apos;../styles/globals.css&apos;
function MyApp({ Component, pageProps }) {
  return &lt;Component {...pageProps} /&gt;
}
export default MyApp
"><code><span class="hljs-keyword">import</span> <span class="hljs-string">'../styles/globals.css'</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">MyApp</span>(<span class="hljs-params">{ Component, pageProps }</span>) {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&#x3C;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /></span></span>
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">MyApp</span>
</code></pre><h3 id="h-step2-indexjsx" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step2 修改index.jsx代码</h3><p>1.<strong>这一步具体每行代码的意义可以到官方看，我这里放最终的代码，方便大家完成任务，但是这对于学习web3是毫无作用的</strong>。将index.jsx的代码更改成如下代码。如图。</p><pre data-type="codeBlock" text="import { NFTCard } from &quot;./nftCard&quot;
import { useState } from &apos;react&apos;

const Home = () =&gt; {
 const [wallet, setWalletAddress] = useState(&quot;&quot;);
 const [collection, setCollectionAddress] = useState(&quot;&quot;);
 const [NFTs, setNFTs] = useState([])
 const [fetchForCollection, setFetchForCollection]=useState(false)


  const fetchNFTs = async() =&gt; {
    let nfts; 
    console.log(&quot;fetching nfts&quot;);
    const api_key = &quot;75dGSnZXuLyiwz-TRVYrJAhhZthlG9Tj&quot;
    const baseURL = `https://eth-mainnet.alchemyapi.io/v2/${api_key}/getNFTs/`;
    var requestOptions = {
        method: &apos;GET&apos;
      };
     
    if (!collection.length) {
    
      const fetchURL = `${baseURL}?owner=${wallet}`;
  
      nfts = await fetch(fetchURL, requestOptions).then(data =&gt; data.json())
    } else {
      console.log(&quot;fetching nfts for collection owned by address&quot;)
      const fetchURL = `${baseURL}?owner=${wallet}&amp;contractAddresses%5B%5D=${collection}`;
      nfts= await fetch(fetchURL, requestOptions).then(data =&gt; data.json())
    }
  
    if (nfts) {
      console.log(&quot;nfts:&quot;, nfts)
      setNFTs(nfts.ownedNfts)
    }
  }
  
  const fetchNFTsForCollection = async () =&gt; {
    if (collection.length) {
      var requestOptions = {
        method: &apos;GET&apos;
      };
      const api_key = &quot;75dGSnZXuLyiwz-TRVYrJAhhZthlG9Tj&quot;
      const baseURL = `https://eth-mainnet.alchemyapi.io/v2/${api_key}/getNFTsForCollection/`;
      const fetchURL = `${baseURL}?contractAddress=${collection}&amp;withMetadata=${&quot;true&quot;}`;
      const nfts = await fetch(fetchURL, requestOptions).then(data =&gt; data.json())
      if (nfts) {
        console.log(&quot;NFTs in collection:&quot;, nfts)
        setNFTs(nfts.nfts)
      }
    }
  }

 return (
   &lt;div className=&quot;flex flex-col items-center justify-center py-8 gap-y-3&quot;&gt;
     &lt;div className=&quot;flex flex-col w-full justify-center items-center gap-y-2&quot;&gt;
     &lt;input disabled={fetchForCollection} type={&quot;text&quot;} placeholder=&quot;Add your wallet address&quot; onChange={e =&gt; setWalletAddress(e.target.value)} value={wallet}&gt;&lt;/input&gt;
       &lt;input type={&quot;text&quot;} placeholder=&quot;Add the collection address&quot;&gt;&lt;/input&gt;
       &lt;label className=&quot;text-gray-600 &quot;&gt;&lt;input onChange={(e)=&gt;{setFetchForCollection(e.target.checked)}} type={&quot;checkbox&quot;} className=&quot;mr-2&quot;&gt;&lt;/input&gt;Fetch for collection&lt;/label&gt;
       &lt;button className={&quot;disabled:bg-slate-500 text-white bg-blue-400 px-4 py-2 mt-3 rounded-sm w-1/5&quot;} onClick={
          () =&gt; {
           if (fetchForCollection) {
             fetchNFTsForCollection()
           }else fetchNFTs()
         }
       }&gt;Let&apos;s go! &lt;/button&gt;
     &lt;/div&gt;
     &lt;div className=&apos;flex flex-wrap gap-y-12 mt-4 w-5/6 gap-x-2 justify-center&apos;&gt;
       {
         NFTs.length &amp;&amp; NFTs.map(nft =&gt; {
           return (
             &lt;NFTCard nft={nft}&gt;&lt;/NFTCard&gt;
           )
         })
       }
     &lt;/div&gt;
   &lt;/div&gt;
 )
}

export default Home
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">NFTCard</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./nftCard"</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> { <span class="hljs-title">useState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'react'</span>

<span class="hljs-title">const</span> <span class="hljs-title">Home</span> <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
 <span class="hljs-title">const</span> [<span class="hljs-title">wallet</span>, <span class="hljs-title">setWalletAddress</span>] <span class="hljs-operator">=</span> <span class="hljs-title">useState</span>(<span class="hljs-string">""</span>);
 const [collection, setCollectionAddress] <span class="hljs-operator">=</span> useState(<span class="hljs-string">""</span>);
 const [NFTs, setNFTs] <span class="hljs-operator">=</span> useState([])
 const [fetchForCollection, setFetchForCollection]<span class="hljs-operator">=</span>useState(<span class="hljs-literal">false</span>)


  const fetchNFTs <span class="hljs-operator">=</span> async() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    let nfts; 
    console.log(<span class="hljs-string">"fetching nfts"</span>);
    const api_key <span class="hljs-operator">=</span> <span class="hljs-string">"75dGSnZXuLyiwz-TRVYrJAhhZthlG9Tj"</span>
    const baseURL <span class="hljs-operator">=</span> `https:<span class="hljs-comment">//eth-mainnet.alchemyapi.io/v2/${api_key}/getNFTs/`;</span>
    <span class="hljs-keyword">var</span> requestOptions <span class="hljs-operator">=</span> {
        method: <span class="hljs-string">'GET'</span>
      };
     
    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>collection.<span class="hljs-built_in">length</span>) {
    
      const fetchURL <span class="hljs-operator">=</span> `${baseURL}?owner<span class="hljs-operator">=</span>${wallet}`;
  
      nfts <span class="hljs-operator">=</span> await fetch(fetchURL, requestOptions).then(data <span class="hljs-operator">=</span><span class="hljs-operator">></span> data.json())
    } <span class="hljs-keyword">else</span> {
      console.log(<span class="hljs-string">"fetching nfts for collection owned by address"</span>)
      const fetchURL <span class="hljs-operator">=</span> `${baseURL}?owner<span class="hljs-operator">=</span>${wallet}<span class="hljs-operator">&#x26;</span>contractAddresses<span class="hljs-operator">%</span>5B<span class="hljs-operator">%</span>5D<span class="hljs-operator">=</span>${collection}`;
      nfts<span class="hljs-operator">=</span> await fetch(fetchURL, requestOptions).then(data <span class="hljs-operator">=</span><span class="hljs-operator">></span> data.json())
    }
  
    <span class="hljs-keyword">if</span> (nfts) {
      console.log(<span class="hljs-string">"nfts:"</span>, nfts)
      setNFTs(nfts.ownedNfts)
    }
  }
  
  const fetchNFTsForCollection <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">if</span> (collection.<span class="hljs-built_in">length</span>) {
      <span class="hljs-keyword">var</span> requestOptions <span class="hljs-operator">=</span> {
        method: <span class="hljs-string">'GET'</span>
      };
      const api_key <span class="hljs-operator">=</span> <span class="hljs-string">"75dGSnZXuLyiwz-TRVYrJAhhZthlG9Tj"</span>
      const baseURL <span class="hljs-operator">=</span> `https:<span class="hljs-comment">//eth-mainnet.alchemyapi.io/v2/${api_key}/getNFTsForCollection/`;</span>
      const fetchURL <span class="hljs-operator">=</span> `${baseURL}?contractAddress<span class="hljs-operator">=</span>${collection}<span class="hljs-operator">&#x26;</span>withMetadata<span class="hljs-operator">=</span>${<span class="hljs-string">"true"</span>}`;
      const nfts <span class="hljs-operator">=</span> await fetch(fetchURL, requestOptions).then(data <span class="hljs-operator">=</span><span class="hljs-operator">></span> data.json())
      <span class="hljs-keyword">if</span> (nfts) {
        console.log(<span class="hljs-string">"NFTs in collection:"</span>, nfts)
        setNFTs(nfts.nfts)
      }
    }
  }

 <span class="hljs-keyword">return</span> (
   <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex flex-col items-center justify-center py-8 gap-y-3"</span><span class="hljs-operator">></span>
     <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex flex-col w-full justify-center items-center gap-y-2"</span><span class="hljs-operator">></span>
     <span class="hljs-operator">&#x3C;</span>input disabled<span class="hljs-operator">=</span>{fetchForCollection} <span class="hljs-keyword">type</span><span class="hljs-operator">=</span>{<span class="hljs-string">"text"</span>} placeholder<span class="hljs-operator">=</span><span class="hljs-string">"Add your wallet address"</span> onChange<span class="hljs-operator">=</span>{e <span class="hljs-operator">=</span><span class="hljs-operator">></span> setWalletAddress(e.target.<span class="hljs-built_in">value</span>)} value<span class="hljs-operator">=</span>{wallet}<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>input<span class="hljs-operator">></span>
       <span class="hljs-operator">&#x3C;</span>input <span class="hljs-keyword">type</span><span class="hljs-operator">=</span>{<span class="hljs-string">"text"</span>} placeholder<span class="hljs-operator">=</span><span class="hljs-string">"Add the collection address"</span><span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>input<span class="hljs-operator">></span>
       <span class="hljs-operator">&#x3C;</span>label className<span class="hljs-operator">=</span><span class="hljs-string">"text-gray-600 "</span><span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span>input onChange<span class="hljs-operator">=</span>{(e)<span class="hljs-operator">=</span><span class="hljs-operator">></span>{setFetchForCollection(e.target.checked)}} <span class="hljs-keyword">type</span><span class="hljs-operator">=</span>{<span class="hljs-string">"checkbox"</span>} className<span class="hljs-operator">=</span><span class="hljs-string">"mr-2"</span><span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>input<span class="hljs-operator">></span>Fetch <span class="hljs-keyword">for</span> collection<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>label<span class="hljs-operator">></span>
       <span class="hljs-operator">&#x3C;</span>button className<span class="hljs-operator">=</span>{<span class="hljs-string">"disabled:bg-slate-500 text-white bg-blue-400 px-4 py-2 mt-3 rounded-sm w-1/5"</span>} onClick<span class="hljs-operator">=</span>{
          () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
           <span class="hljs-keyword">if</span> (fetchForCollection) {
             fetchNFTsForCollection()
           }<span class="hljs-keyword">else</span> fetchNFTs()
         }
       }<span class="hljs-operator">></span>Let<span class="hljs-string">'s go! &#x3C;/button>
     &#x3C;/div>
     &#x3C;div className='</span>flex flex<span class="hljs-operator">-</span>wrap gap<span class="hljs-operator">-</span>y<span class="hljs-number">-12</span> mt<span class="hljs-number">-4</span> w<span class="hljs-number">-5</span><span class="hljs-operator">/</span><span class="hljs-number">6</span> gap<span class="hljs-operator">-</span>x<span class="hljs-number">-2</span> justify<span class="hljs-operator">-</span>center<span class="hljs-string">'>
       {
         NFTs.length &#x26;&#x26; NFTs.map(nft => {
           return (
             &#x3C;NFTCard nft={nft}>&#x3C;/NFTCard>
           )
         })
       }
     &#x3C;/div>
   &#x3C;/div>
 )
}

export default Home
</span></code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ad31cb506e7a8802fb62da5066e7056214ed39d16a8e018d91051e4d0fde6a1e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-step3" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step3 创建一个新的炼金术应用程序</h3><p>1.进入<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://alchemy.com/?a=cn-road-to-week-four">alchemy.com</a> ，点击create app。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/caf7e0ef08cf5a82808fd82209dd36042b78a39625efb711a19991c42e4c2787.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>2.输入信息，点击create app。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/46342f9b5c3ef2448435601c0d45c81369a1b9b0e15e13831b8f431976081cfb.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>3.点击view key，将红框内的API KEY复制下来。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6c57e9752e73e6c265948bf06095544a9a505099aef7b5cb16f3880d974c2c99.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>4.回到vscode，在index.jsx中ctrl+f搜索const api_key，应该会搜索到2处，全部改成上一步复制的代码，然后保存。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6f9b269790377ff715e61a430492fece352ab94b236e9952baaa24c06bdd3eb8.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-step4-nft-card" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step4 创建 NFT Card 组件</h3><p>1.在pages下面新建一个名为nftCard.jsx的文件，并将下面的代码粘贴进去。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/06019ef6b110a73687a8010bea83999de72e2f2bbf23fe09a017d6078cf2fadc.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><pre data-type="codeBlock" text="export const NFTCard = ({ nft }) =&gt; {

    return (
        &lt;div className=&quot;w-1/4 flex flex-col &quot;&gt;
        &lt;div className=&quot;rounded-md&quot;&gt;
            &lt;img className=&quot;object-cover h-128 w-full rounded-t-md&quot; src={nft.media[0].gateway} &gt;&lt;/img&gt;
        &lt;/div&gt;
        &lt;div className=&quot;flex flex-col y-gap-2 px-2 py-3 bg-slate-100 rounded-b-md h-110 &quot;&gt;
            &lt;div className=&quot;&quot;&gt;
                &lt;h2 className=&quot;text-xl text-gray-800&quot;&gt;{nft.title}&lt;/h2&gt;
                &lt;p className=&quot;text-gray-600&quot;&gt;Id: {nft.id.tokenId}&lt;/p&gt;
                &lt;p className=&quot;text-gray-600&quot; &gt;{nft.contract.address}&lt;/p&gt;
            &lt;/div&gt;

            &lt;div className=&quot;flex-grow mt-2&quot;&gt;
                &lt;p className=&quot;text-gray-600&quot;&gt;{nft.description}&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;

    &lt;/div&gt;
    )
}
"><code>export const NFTCard <span class="hljs-operator">=</span> ({ nft }) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {

    <span class="hljs-keyword">return</span> (
        <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"w-1/4 flex flex-col "</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"rounded-md"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>img className<span class="hljs-operator">=</span><span class="hljs-string">"object-cover h-128 w-full rounded-t-md"</span> src<span class="hljs-operator">=</span>{nft.media[<span class="hljs-number">0</span>].gateway} <span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>img<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex flex-col y-gap-2 px-2 py-3 bg-slate-100 rounded-b-md h-110 "</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">""</span><span class="hljs-operator">></span>
                <span class="hljs-operator">&#x3C;</span>h2 className<span class="hljs-operator">=</span><span class="hljs-string">"text-xl text-gray-800"</span><span class="hljs-operator">></span>{nft.title}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
                <span class="hljs-operator">&#x3C;</span>p className<span class="hljs-operator">=</span><span class="hljs-string">"text-gray-600"</span><span class="hljs-operator">></span>Id: {nft.id.tokenId}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
                <span class="hljs-operator">&#x3C;</span>p className<span class="hljs-operator">=</span><span class="hljs-string">"text-gray-600"</span> <span class="hljs-operator">></span>{nft.contract.<span class="hljs-built_in">address</span>}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex-grow mt-2"</span><span class="hljs-operator">></span>
                <span class="hljs-operator">&#x3C;</span>p className<span class="hljs-operator">=</span><span class="hljs-string">"text-gray-600"</span><span class="hljs-operator">></span>{nft.description}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    )
}
</code></pre><h3 id="h-step5" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step5 运行测试</h3><p>1.在控制台输入cd nameoftheproject，按回车，然后输入npm run dev，如图所示即可。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/322d0d6ed18c8b5f415cd0b05d17b518f89955ae39c85adc8b3f278bd9e7871f.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>2.然后将<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://localhost:3000/">http://localhost:3000</a> （我这里是这个链接，我看官方视频是3001，可能会不一样啊，我也不知道哈哈），就是上面那个图的第一行的链接，复制到浏览器。出现下图即可。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b711e04c1ad87ab0025e0f5ee7e488b2c5116c0ca9d5c4f7fb4aba5f73d528d6.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>3.输入你自己的地址，点击let‘s go，下面出现图片即可。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c6bf09f787d31299fa7d38dae764a8450dce232180750051e404f81ba6227352.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-step6-github" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step6 上传至github</h3><h3 id="h-step7" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step7 项目提交</h3><p>直接填写上传的项目的github网址即可。</p>]]></content:encoded>
            <author>monkey-11@newsletter.paragraph.com (Monkey)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy第三周任务-使用Polygon链上元数据制作 NFT]]></title>
            <link>https://paragraph.com/@monkey-11/alchemy-polygon-nft</link>
            <guid>lhNS1X3Ym41cOF2zdFGz</guid>
            <pubDate>Fri, 14 Oct 2022 06:35:49 GMT</pubDate>
            <description><![CDATA[我们都知道创建 NFT 时，最好将元数据存储在集中式对象存储或IPFS等分散式解决方案中，以避免直接在链上存储大量数据（如图像和 JSON 对象）产生的巨额 Gas 费用。 但这有一个问题： 不将元数据存储在区块链上将使您无法通过智能合约与之交互，因为区块无法与“外部世界”通信。**如果我们想直接从我们的智能合约更新我们的元数据，我们需要将其存储在链上，但是汽油费呢？幸运的是，像 Polygon 这样的 L2 链可以提供帮助，大大降低了 Gas 成本，并引入了许多优势，使开发人员能够扩展其应用程序的功能。 在本教程中，学习如何创建区块链游戏的基础知识，开发一个完全动态的 NFT，其链上元数据会根据您与它的交互而变化，并将其部署在Polygon Mumbai上以降低汽油费。 更准确地说，您将学习：如何在链上存储 NFT 元数据什么是 Polygon 以及为什么降低 Gas 费用很重要。如何在 Polygon Mumbai 上部署如何处理和存储链上 SVG 图像和 JSON 对象如何根据您与 NFT 的交互来修改元数据1.Polygon PoS - 更低的 Gas 费用和更快的交易P...]]></description>
            <content:encoded><![CDATA[<p>我们都知道创建 NFT 时，最好<strong>将元数据存储在集中式对象存储</strong>或<strong>IPFS</strong>等分散式解决方案中，以避免直接在链上存储大量数据（如图像和 JSON 对象）产生的巨额 Gas 费用。</p><p><strong>但这有一个问题：</strong></p><p>不将元数据存储在区块链上将使您无法通过智能合约与之交互，因为区块无法与“外部世界”通信。**如果我们想直接从我们的智能合约更新我们的元数据，我们需要将其存储在链上，但是汽油费呢？幸运的是，像 Polygon 这样的 L2 链可以提供帮助，<strong>大大降低了 Gas 成本</strong>，并引入了许多优势，使开发人员能够扩展其应用程序的功能。</p><p>在本教程中，学习如何<strong>创建区块链游戏的基础知识</strong>，开发一个完全动态的 NFT，其链上元数据会根据您与它的交互而变化，并将其部署在<strong>Polygon Mumbai</strong>上以降低汽油费。</p><p><strong>更准确地说，您将学习：</strong></p><ul><li><p>如何在链上存储 NFT 元数据</p></li><li><p>什么是 Polygon 以及为什么降低 Gas 费用很重要。</p></li><li><p>如何在 Polygon Mumbai 上部署</p></li><li><p>如何处理和存储链上 SVG 图像和 JSON 对象</p></li><li><p>如何根据您与 NFT 的交互来修改元数据</p></li></ul><h2 id="h-1polygon-pos-gas" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1.Polygon PoS - 更低的 Gas 费用和更快的交易</h2><p>Polygon 是一个去<strong>中心化的 EVM 兼容扩展平台</strong>，使开发人员能够在不牺牲安全性的情况下以低交易费用构建可扩展的用户友好型 DApp。</p><p>它属于被描述为**第 2 层链 (L2)**的一组链，这意味着它建立在以太坊之上，以解决一些表征它的问题 - 同时依赖它来运行。</p><p>众所周知，以太坊既不快也不便宜，在其上部署智能合约可能会迅速变得非常昂贵，这就是<strong>Polygon</strong>或<strong>Optimism</strong>等 L2 解决方案发挥作用的地方。</p><p>例如，多边形有两个主要优点：</p><ul><li><p><strong>更快的交易</strong>（65,000 tx/秒 vs ~14）</p></li><li><p>每笔交易的<strong>gas 成本比以太坊低</strong>约 10,000 倍</p></li></ul><p>第二个正是我们在 Polygon 上部署带有链上元数据的 NFT 智能合约的原因。一方面，如果在以太坊上存储我们的元数据时，我们可以预期每笔交易花费数百美元，<strong>那么在 Polygon 上它的成本不会超过几美分。</strong></p><h3 id="h-11-polygon-mumbai-metamask" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.1将 Polygon Mumbai 添加到您的 Metamask 钱包</h3><p>首先，让我们<strong>将 Polygon Mumbai 添加到我们的 Metamask 钱包中。</strong></p><p>导航<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mumbai.polygonscan.com/">测试polygon</a>网络并向下滚动到页面底部。您将看到**“添加多边形网络”按钮**，单击它并确认您要将其添加到 Metamask：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4d6a86a82be826c13f928c8a53ed5dcc20c0807af1f8ceccc81bea3bb4289628.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-12-matic-nft" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.2获取免费的 Matic 以部署NFT 智能合约</h3><p>我们去到<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mumbaifaucet.com/">mumbaifaucet.com</a>获取测试币，输入我们的地址获取测试币即可</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/09a872a0acd534ce821162a069ca405cb32ce86abcb7b1f8f2b137c6a9df7d57.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>10-20 秒后，会看到 MATIC 出现在 Metamask 钱包中。</p><h3 id="h-21" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.1项目设置</h3><pre data-type="codeBlock" text="# 新建一个文件夹用于此次项目的搭建
mkdir ChainBattled
cd ChainBattled
# 安装yarn并查看版本
npm install -g yarn
yarn --version
yarn add hardhat
# 初始化项目
npx hardhat init
"><code><span class="hljs-comment"># 新建一个文件夹用于此次项目的搭建</span>
<span class="hljs-built_in">mkdir</span> ChainBattled
<span class="hljs-built_in">cd</span> ChainBattled
<span class="hljs-comment"># 安装yarn并查看版本</span>
npm install -g yarn
yarn --version
yarn add hardhat
<span class="hljs-comment"># 初始化项目</span>
npx hardhat init
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e59b95d646a9b570c125a057abb35e3f6dfb6ee4dac2736627be3af38d0fd372.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>现在我们需要安装<strong>OpenZeppelin</strong>包来访问<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts/4.x/api/token/erc721">ERC721 智能合约</a>我们将使用该标准作为模板来构建我们的 NFT 智能合约。</p><pre data-type="codeBlock" text="yarn add @openzeppelin/contracts
"><code>yarn <span class="hljs-keyword">add</span> <span class="hljs-variable">@openzeppelin</span><span class="hljs-operator">/</span>contracts
</code></pre><h3 id="h-22-hardhatconfigjs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.2修改 hardhat.config.js 文件</h3><p>将我们的hardhat.config.js 文件修改如下：</p><pre data-type="codeBlock" text="require(&quot;dotenv&quot;).config();
require(&quot;@nomiclabs/hardhat-waffle&quot;);
require(&quot;@nomiclabs/hardhat-etherscan&quot;);

module.exports = {
  solidity: &quot;0.8.10&quot;,
  networks: {
    mumbai: {
      url: process.env.TESTNET_RPC,
      accounts: [process.env.PRIVATE_KEY]
    },
  },
  etherscan: {
    apiKey: process.env.POLYGONSCAN_API_KEY
  }
};
"><code><span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();
<span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomiclabs/hardhat-waffle"</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomiclabs/hardhat-etherscan"</span>);

module.exports <span class="hljs-operator">=</span> {
  solidity: <span class="hljs-string">"0.8.10"</span>,
  networks: {
    mumbai: {
      url: process.env.TESTNET_RPC,
      accounts: [process.env.PRIVATE_KEY]
    },
  },
  etherscan: {
    apiKey: process.env.POLYGONSCAN_API_KEY
  }
};
</code></pre><h3 id="h-23" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.3开发智能合约</h3><p>在 contracts 文件夹中，创建一个新文件并将其命名为“ChainBattles.sol”。</p><p>与往常一样，我们需要指定<strong>SPDX-Licence-Identifier</strong>、<strong>pragma ，并从OpenZeppelin</strong>导入几个库，我们将用作智能合约的基础，合约内容如下：</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Counters.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Strings.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Base64.sol&quot;;

contract ChainBattles is ERC721URIStorage  {
    using Strings for uint256;
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    mapping(uint256 =&gt; uint256) public tokenIdToLevels;

    constructor() ERC721 (&quot;Chain Battles&quot;, &quot;CBTLS&quot;){
    }

    function generateCharacter(uint256 tokenId) public returns(string memory){

    bytes memory svg = abi.encodePacked(
        &apos;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; preserveAspectRatio=&quot;xMinYMin meet&quot; viewBox=&quot;0 0 350 350&quot;&gt;&apos;,
        &apos;&lt;style&gt;.base { fill: white; font-family: serif; font-size: 14px; }&lt;/style&gt;&apos;,
        &apos;&lt;rect width=&quot;100%&quot; height=&quot;100%&quot; fill=&quot;black&quot; /&gt;&apos;,
        &apos;&lt;text x=&quot;50%&quot; y=&quot;40%&quot; class=&quot;base&quot; dominant-baseline=&quot;middle&quot; text-anchor=&quot;middle&quot;&gt;&apos;,&quot;Warrior&quot;,&apos;&lt;/text&gt;&apos;,
        &apos;&lt;text x=&quot;50%&quot; y=&quot;50%&quot; class=&quot;base&quot; dominant-baseline=&quot;middle&quot; text-anchor=&quot;middle&quot;&gt;&apos;, &quot;Levels: &quot;,getLevels(tokenId),&apos;&lt;/text&gt;&apos;,
        &apos;&lt;/svg&gt;&apos;
    );
    return string(
        abi.encodePacked(
            &quot;data:image/svg+xml;base64,&quot;,
            Base64.encode(svg)
        )    
    );
  }
function getLevels(uint256 tokenId) public view returns (string memory) {
    uint256 levels = tokenIdToLevels[tokenId];
    return levels.toString();
}
 function getTokenURI(uint256 tokenId) public returns (string memory){
    bytes memory dataURI = abi.encodePacked(
        &apos;{&apos;,
            &apos;&quot;name&quot;: &quot;Chain Battles #&apos;, tokenId.toString(), &apos;&quot;,&apos;,
            &apos;&quot;description&quot;: &quot;Battles on chain&quot;,&apos;,
            &apos;&quot;image&quot;: &quot;&apos;, generateCharacter(tokenId), &apos;&quot;&apos;,
        &apos;}&apos;
    );
    return string(
        abi.encodePacked(
            &quot;data:application/json;base64,&quot;,
            Base64.encode(dataURI)
        )
    );
}
function mint() public {
    _tokenIds.increment();
    uint256 newItemId = _tokenIds.current();
    _safeMint(msg.sender, newItemId);
    tokenIdToLevels[newItemId] = 0;
    _setTokenURI(newItemId, getTokenURI(newItemId));
}

function train(uint256 tokenId) public{
  require(_exists(tokenId),&quot;The tokenId does not exist&quot;);
  require(_isApprovedOrOwner(msg.sender, tokenId),&quot;You are not the owner of the NFT&quot;); 
  tokenIdToLevels[tokenId] += 1;
  _setTokenURI(tokenId, getTokenURI(tokenId));
}
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/utils/Counters.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/utils/Strings.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/utils/Base64.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">ChainBattles</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC721URIStorage</span>  </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">Strings</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">uint256</span></span>;
    <span class="hljs-keyword">using</span> <span class="hljs-title">Counters</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">Counters</span>.<span class="hljs-title">Counter</span>;
    Counters.Counter <span class="hljs-keyword">private</span> _tokenIds;

    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> tokenIdToLevels;

    <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">"Chain Battles"</span>, <span class="hljs-string">"CBTLS"</span></span>)</span>{
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateCharacter</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span></span>)</span>{

    <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> svg <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(
        <span class="hljs-string">'&#x3C;svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350">'</span>,
        <span class="hljs-string">'&#x3C;style>.base { fill: white; font-family: serif; font-size: 14px; }&#x3C;/style>'</span>,
        <span class="hljs-string">'&#x3C;rect width="100%" height="100%" fill="black" />'</span>,
        <span class="hljs-string">'&#x3C;text x="50%" y="40%" class="base" dominant-baseline="middle" text-anchor="middle">'</span>,<span class="hljs-string">"Warrior"</span>,<span class="hljs-string">'&#x3C;/text>'</span>,
        <span class="hljs-string">'&#x3C;text x="50%" y="50%" class="base" dominant-baseline="middle" text-anchor="middle">'</span>, <span class="hljs-string">"Levels: "</span>,getLevels(tokenId),<span class="hljs-string">'&#x3C;/text>'</span>,
        <span class="hljs-string">'&#x3C;/svg>'</span>
    );
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>(
        <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(
            <span class="hljs-string">"data:image/svg+xml;base64,"</span>,
            Base64.encode(svg)
        )    
    );
  }
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getLevels</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</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 class="hljs-keyword">memory</span></span>) </span>{
    <span class="hljs-keyword">uint256</span> levels <span class="hljs-operator">=</span> tokenIdToLevels[tokenId];
    <span class="hljs-keyword">return</span> levels.toString();
}
 <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTokenURI</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span></span>)</span>{
    <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> dataURI <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(
        <span class="hljs-string">'{'</span>,
            <span class="hljs-string">'"name": "Chain Battles #'</span>, tokenId.toString(), <span class="hljs-string">'",'</span>,
            <span class="hljs-string">'"description": "Battles on chain",'</span>,
            <span class="hljs-string">'"image": "'</span>, generateCharacter(tokenId), <span class="hljs-string">'"'</span>,
        <span class="hljs-string">'}'</span>
    );
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>(
        <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(
            <span class="hljs-string">"data:application/json;base64,"</span>,
            Base64.encode(dataURI)
        )
    );
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
    _tokenIds.increment();
    <span class="hljs-keyword">uint256</span> newItemId <span class="hljs-operator">=</span> _tokenIds.current();
    _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, newItemId);
    tokenIdToLevels[newItemId] <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
    _setTokenURI(newItemId, getTokenURI(newItemId));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">train</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span></span>{
  <span class="hljs-built_in">require</span>(_exists(tokenId),<span class="hljs-string">"The tokenId does not exist"</span>);
  <span class="hljs-built_in">require</span>(_isApprovedOrOwner(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, tokenId),<span class="hljs-string">"You are not the owner of the NFT"</span>); 
  tokenIdToLevels[tokenId] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
  _setTokenURI(tokenId, getTokenURI(tokenId));
}
}
</code></pre><p>我们在上面将合约全部完善了，也新增了 4 个不同的功能：</p><ul><li><p>**generateCharacter：**生成和更新我们 NFT 的 SVG 图像</p></li><li><p>**getLevels：**获取 NFT 的当前级别</p></li><li><p>**getTokenURI ：**获取 NFT 的 TokenURI</p></li><li><p>**mint:**到 mint - 当然</p></li><li><p>**train：**训练 NFT 并提高其等级</p></li></ul><p>首先，让我们在项目的根文件夹中新建一个 .env 文件，并添加以下变量：</p><pre data-type="codeBlock" text="TESTNET_RPC=&quot;&quot;
PRIVATE_KEY=&quot;&quot;
POLYGONSCAN_API_KEY=&quot;&quot;
"><code><span class="hljs-attr">TESTNET_RPC</span>=<span class="hljs-string">""</span>
<span class="hljs-attr">PRIVATE_KEY</span>=<span class="hljs-string">""</span>
<span class="hljs-attr">POLYGONSCAN_API_KEY</span>=<span class="hljs-string">""</span>
</code></pre><p>然后，导航到<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/">alchemy.com</a>并创建一个新的 Polygon Mumbai 应用程序：</p><p>单击新创建的应用程序，复制 API HTTP URL，并将 API 作为“ <strong>TESTNET_RPC</strong> ”值粘贴到我们在上面创建的 .env 文件中。</p><p><strong>打开您的Metamask</strong>钱包，点击三个点菜单 &gt; 帐户详细信息 &gt; 并将您的私钥复制粘贴为.<code>.env</code></p><p>最后，继续<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://polygonscan.com/">Polygon,</a>并创建一个新帐户，登录后，进入<strong>个人资料菜单</strong>并单击 API Keys，如果没有则需要新建一个：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a118cd74182eec056389e210a83c632d1928e5aee373c2be5724d133bc39cbb1.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>现在将 Api-Key 令牌复制粘贴为 .env 中的“ <strong>POLYGONSCAN <em>API_KEY</em></strong> <em>”值。最终结果如下：</em></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e7f56513399c6ee9ac7cbaa8372c0b53a7c368cc41df923a4d1f4e0bc0f16966.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><em>在部署我们的智能合约之前的最后一步，我们需要</em><strong><em>创建部署脚本。</em></strong></p><h3 id="h-31" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.1创建部署脚本</h3><p>首先安装依赖</p><pre data-type="codeBlock" text="npm install dotenv
npm install @nomiclabs/hardhat-waffle
"><code>npm install dotenv
npm install @nomiclabs<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>waffle
</code></pre><p>我们将deploy.js的脚本替换如下：</p><pre data-type="codeBlock" text="const main = async () =&gt; {
  try {
    const nftContractFactory = await hre.ethers.getContractFactory(
      &quot;ChainBattles&quot;
    );
    const nftContract = await nftContractFactory.deploy();
    await nftContract.deployed();

    console.log(&quot;Contract deployed to:&quot;, nftContract.address);
    process.exit(0);
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};
  
main();
"><code>const main <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  <span class="hljs-keyword">try</span> {
    const nftContractFactory <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(
      <span class="hljs-string">"ChainBattles"</span>
    );
    const nftContract <span class="hljs-operator">=</span> await nftContractFactory.deploy();
    await nftContract.deployed();

    console.log(<span class="hljs-string">"Contract deployed to:"</span>, nftContract.<span class="hljs-built_in">address</span>);
    process.exit(<span class="hljs-number">0</span>);
  } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    console.log(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    process.exit(<span class="hljs-number">1</span>);
  }
};
  
main();
</code></pre><h3 id="h-32" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.2编译和部署智能合约</h3><p>当我们把脚本写好后，编译智能合约，只需在项目内的终端中运行以下命令：</p><pre data-type="codeBlock" text="npx hardhat compile
"><code>npx hardhat <span class="hljs-built_in">compile</span>
</code></pre><p>如果一切按预期进行，你将<strong>在 artifacts 文件夹中看到已编译的智能合约。</strong></p><p>现在，让我们在运行的 Polygon Mumbai 链上部署智能合约</p><pre data-type="codeBlock" text="npx hardhat run scripts/deploy.js --network mumbai
"><code>npx hardhat run scripts<span class="hljs-operator">/</span>deploy.js <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network mumbai
</code></pre><p>如果一切正常你将在页面看到合约的地址</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3546623bdc3d646e945ef92327471176328142550136312b95c843ead704b590.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-33-polygon-scan" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.3在 Polygon Scan 上检查智能合约</h3><p>复制刚刚部署的智能合约的地址，去<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mumbai.polygonscan.com/">测试polygon</a>网络，然后**在搜索栏中粘贴智能合约的地址。**进入智能合约页面后，单击“合约”选项卡。你会注意到合约代码不可读：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f1d9c2f9c207165fca1b8cacb96aef3950ebc0b273f97c15d1e8f2e82ff2ff33.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>这是因为我们还没有验证我们的代码。</strong></p><p>为了验证我们的智能合约，我们需要回到我们的项，并在终端中运行以下代码：</p><pre data-type="codeBlock" text="# npx hardhat verify --network mumbai 合约地址
npx hardhat verify --network mumbai 
"><code># npx hardhat verify <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network mumbai 合约地址
npx hardhat verify <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network mumbai 
</code></pre><p>0xE49617678DeA173D147e484a10f7dbcc4aE568e1</p><p>报错信息：</p><pre data-type="codeBlock" text="Error in plugin @nomiclabs/hardhat-etherscan: Failed to send contract verification request.
Endpoint URL: https://api-testnet.polygonscan.com/api
Reason: read ECONNRESET
"><code>Error in plugin <span class="hljs-keyword">@nomiclabs</span>/<span class="hljs-attribute">hardhat-etherscan</span>: Failed to send contract verification request.
Endpoint <span class="hljs-attribute">URL</span>: <span class="hljs-attribute">https</span>://api-testnet.polygonscan.com/api
<span class="hljs-attribute">Reason</span>: read ECONNRESET
</code></pre><p>通过网站添加方法，点击立即验证即可：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/29dcf0439b0ac777b0dee89ef861b83216148686631111f7bd32f1a0ee6963e4.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-34" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.4通过多边形扫描与您的智能合约交互</h3><p>现在智能合约已经通过验证，mumbai.polygonscan.com 将在其附近显示一个绿色小勾：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/89754d9ad2470acfb2068704d3e57e9ea13044aa2777ef3a39439a8114d370d9.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0486c8cd07473c45873a2127267463d700f455a70b1b5a45b8cf40f2a21707e1.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>然后寻找“min​​t”函数并点击Write：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/32088f7a1758ebee67a4d30230b9febeadfdfae6ba1d3f1622b4d524a23078e0.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>这将打开一个 Metamask 弹出窗口，要求支付 gas 费用，单击签名按钮。恭喜！您刚刚铸造了您的第一个动态 NFT - 让我们转移到 OpenSea 测试网来现场查看它。</p><h2 id="h-4-opensea-nft" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">4.在 OpenSea 上查看您的动态 NFT</h2><p>复制智能合约地址，前往<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnets.opensea.io/">testnet.opensea.com</a>，并将其粘贴到搜索栏中：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fcf79355b7791298b9b379ffb4e32aadb5b2f98f1f0b7fc4c21a78ac676017e7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>如果一切正常，现在应该会看到您的 NFT 显示在 OpenSea 上，其中包含动态图像、标题和描述。</p><h3 id="h-41-nft-nft" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.1更新动态 NFT 图像训练 NFT</h3><p>导航回<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mumbai.polygonscan.com/">测试.polygons</a>网络，<strong>点击合约标签 &gt; 写合约</strong>并寻找“train”功能。</p><p>插入您的 NFT 的 ID - 在这种情况下为“1”，因为我们只铸造了一个，然后<strong>单击写入：</strong></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/89efa6480a35a5a41cfa128a9b2e1becd766571fc27db3c95c20016cbedaaf65.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>然后回到<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnets.opensea.io/">testnets.opensea.com</a>并刷新页面：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7747f17de4d3c50ccec549179f471c89c5e1f6bcb9659bc40ea9e27297158946.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>monkey-11@newsletter.paragraph.com (Monkey)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3-第二周文本教程]]></title>
            <link>https://paragraph.com/@monkey-11/alchemy-the-road-to-web3</link>
            <guid>DGzomQCcT4ITxhaTlu8a</guid>
            <pubDate>Fri, 14 Oct 2022 03:09:55 GMT</pubDate>
            <description><![CDATA[今天我们一起来看看第二周任务。首先看看任务的目标是什么？在本教程中，将学习如何使用Alchemy、Hardhat、Ethers.js开发和部署去中心化的“给我买杯咖啡”智能合约，允许访问者发送（假）ETH 作为提示并留下好消息。话不多说，我们现在就开始第二周课程吧。 1.先决条件npm (npx) version 8.5.5node version 16.13.1如果你会js代码更好2.创建项目#创建一个项目目录并且初始化package.json mkdir BuyMeACoffee-contracts cd BuyMeACoffee-contracts npm init -y 创建目录创建目录 创建目录# 使用hardhat生成项目框架 npx hardhat # 该命令建议执行，因为依赖可能有问题 npm install --save-dev hardhat@^2.9.3 @nomiclabs/hardhat-waffle@^2.0.0 ethereum-waffle@^3.0.0 chai@^4.2.0 @nomiclabs/hardhat-ethers@^2.0.0 et...]]></description>
            <content:encoded><![CDATA[<p>今天我们一起来看看第二周任务。首先看看任务的目标是什么？在本教程中，将学习<strong>如何使用Alchemy、Hardhat、Ethers.js</strong>开发和部署去中心化的“给我买杯咖啡”智能合约，允许访问者发送（假）ETH 作为提示并留下好消息。话不多说，我们现在就开始第二周课程吧。</p><p><strong>1.先决条件</strong></p><ul><li><p><code>npm</code> (<code>npx</code>) version 8.5.5</p></li><li><p><code>node</code> version 16.13.1</p></li><li><p>如果你会js代码更好</p></li></ul><p><strong>2.创建项目</strong></p><pre data-type="codeBlock" text="#创建一个项目目录并且初始化package.json
mkdir BuyMeACoffee-contracts
cd BuyMeACoffee-contracts
npm init -y
"><code>#创建一个项目目录并且初始化package.json
mkdir BuyMeACoffee<span class="hljs-operator">-</span>contracts
cd BuyMeACoffee<span class="hljs-operator">-</span>contracts
npm init <span class="hljs-operator">-</span>y
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/db6c13cc2c94a79108ef0eba0fabff927aa052d6e444ebbcd21398db45362997.png" alt="创建目录" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">创建目录</figcaption></figure><p>创建目录</p><p>创建目录</p><pre data-type="codeBlock" text="# 使用hardhat生成项目框架
npx hardhat
# 该命令建议执行，因为依赖可能有问题
npm install --save-dev hardhat@^2.9.3 @nomiclabs/hardhat-waffle@^2.0.0 ethereum-waffle@^3.0.0 chai@^4.2.0 @nomiclabs/hardhat-ethers@^2.0.0 ethers@^5.0.0

# 当你创建成功后目录如下所示：
.
├── README.md
├── contracts
├── hardhat.config.js
├── node_modules
├── package-lock.json
├── package.json
├── scripts
└── test

contracts- 您的智能合约所在的文件夹
  在这个项目中，我们将只创建一个，来组织我们的逻辑BuyMeACoffee
scripts- 您的安全帽 javscript 脚本所在的文件夹
  我们将编写逻辑deploy
  示例脚本buy-coffee
  和一个兑现我们小费的脚本withdraw
hardhat.config.js- 带有solidity版本和部署设置的配置文件
"><code># 使用hardhat生成项目框架
npx hardhat
# 该命令建议执行，因为依赖可能有问题
npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save<span class="hljs-operator">-</span>dev hardhat@<span class="hljs-operator">^</span><span class="hljs-number">2.9</span><span class="hljs-number">.3</span> @nomiclabs<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>waffle@<span class="hljs-operator">^</span><span class="hljs-number">2.0</span><span class="hljs-number">.0</span> ethereum<span class="hljs-operator">-</span>waffle@<span class="hljs-operator">^</span><span class="hljs-number">3.0</span><span class="hljs-number">.0</span> chai@<span class="hljs-operator">^</span><span class="hljs-number">4.2</span><span class="hljs-number">.0</span> @nomiclabs<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>ethers@<span class="hljs-operator">^</span><span class="hljs-number">2.0</span><span class="hljs-number">.0</span> ethers@<span class="hljs-operator">^</span><span class="hljs-number">5.0</span><span class="hljs-number">.0</span>

# 当你创建成功后目录如下所示：
.
├── README.md
├── contracts
├── hardhat.config.js
├── node_modules
├── package<span class="hljs-operator">-</span>lock.json
├── package.json
├── scripts
└── test

contracts<span class="hljs-operator">-</span> 您的智能合约所在的文件夹
  在这个项目中，我们将只创建一个，来组织我们的逻辑BuyMeACoffee
scripts<span class="hljs-operator">-</span> 您的安全帽 javscript 脚本所在的文件夹
  我们将编写逻辑deploy
  示例脚本buy<span class="hljs-operator">-</span>coffee
  和一个兑现我们小费的脚本withdraw
hardhat.config.js- 带有solidity版本和部署设置的配置文件
</code></pre><p><strong>3.开始开发项目</strong></p><p>我们可以使用任意的vim方式打开上面创建的项目，这里就用VScode了，如果有需要的可以去官网下载</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b8feee6b6d3085f26ffbc838d00939211853042eccb90510f93647722039c0c1.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>我们可以看到生成的目录已经有了一个合约，我们只需要去替换即可，首先将合约文件名替换为 BuyMeACoffee.sol 同时将合约内容替换成下面的。</p><pre data-type="codeBlock" text="//SPDX-License-Identifier: Unlicense

// contracts/BuyMeACoffee.sol
pragma solidity ^0.8.0;

// Switch this to your own contract address once deployed, for bookkeeping!

contract BuyMeACoffee {
    // Event to emit when a Memo is created.
    event NewMemo(
        address indexed from,
        uint256 timestamp,
        string name,
        string message
    );
    
    // Memo struct.
    struct Memo {
        address from;
        uint256 timestamp;
        string name;
        string message;
    }
    
    // Address of contract deployer. Marked payable so that
    // we can withdraw to this address later.
    address payable owner;

    // List of all memos received from coffee purchases.
    Memo[] memos;

    constructor() {
        // Store the address of the deployer as a payable address.
        // When we withdraw funds, we&apos;ll withdraw here.
        owner = payable(msg.sender);
    }

    /**
     * @dev fetches all stored memos
     */
    function getMemos() public view returns (Memo[] memory) {
        return memos;
    }

    /**
     * @dev buy a coffee for owner (sends an ETH tip and leaves a memo)
     * @param _name name of the coffee purchaser
     * @param _message a nice message from the purchaser
     */
    function buyCoffee(string memory _name, string memory _message) public payable {
        // Must accept more than 0 ETH for a coffee.
        require(msg.value &gt; 0, &quot;can&apos;t buy coffee for free!&quot;);

        // Add the memo to storage!
        memos.push(Memo(
            msg.sender,
            block.timestamp,
            _name,
            _message
        ));

        // Emit a NewMemo event with details about the memo.
        emit NewMemo(
            msg.sender,
            block.timestamp,
            _name,
            _message
        );
    }

    /**
     * @dev send the entire balance stored in this contract to the owner
     */
    function withdrawTips() public {
        require(owner.send(address(this).balance));
    }
}
"><code><span class="hljs-comment">//SPDX-License-Identifier: Unlicense</span>

<span class="hljs-comment">// contracts/BuyMeACoffee.sol</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-comment">// Switch this to your own contract address once deployed, for bookkeeping!</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">BuyMeACoffee</span> </span>{
    <span class="hljs-comment">// Event to emit when a Memo is created.</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NewMemo</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> <span class="hljs-keyword">from</span>,
        <span class="hljs-keyword">uint256</span> timestamp,
        <span class="hljs-keyword">string</span> name,
        <span class="hljs-keyword">string</span> message
    </span>)</span>;
    
    <span class="hljs-comment">// Memo struct.</span>
    <span class="hljs-keyword">struct</span> <span class="hljs-title">Memo</span> {
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>;
        <span class="hljs-keyword">uint256</span> timestamp;
        <span class="hljs-keyword">string</span> name;
        <span class="hljs-keyword">string</span> message;
    }
    
    <span class="hljs-comment">// Address of contract deployer. Marked payable so that</span>
    <span class="hljs-comment">// we can withdraw to this address later.</span>
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">payable</span> owner;

    <span class="hljs-comment">// List of all memos received from coffee purchases.</span>
    Memo[] memos;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-comment">// Store the address of the deployer as a payable address.</span>
        <span class="hljs-comment">// When we withdraw funds, we'll withdraw here.</span>
        owner <span class="hljs-operator">=</span> <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
    }

    <span class="hljs-comment">/**
     * @dev fetches all stored memos
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMemos</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</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">Memo[] <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> memos;
    }

    <span class="hljs-comment">/**
     * @dev buy a coffee for owner (sends an ETH tip and leaves a memo)
     * @param _name name of the coffee purchaser
     * @param _message a nice message from the purchaser
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buyCoffee</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _name, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _message</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        <span class="hljs-comment">// Must accept more than 0 ETH for a coffee.</span>
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"can't buy coffee for free!"</span>);

        <span class="hljs-comment">// Add the memo to storage!</span>
        memos.<span class="hljs-built_in">push</span>(Memo(
            <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>,
            _name,
            _message
        ));

        <span class="hljs-comment">// Emit a NewMemo event with details about the memo.</span>
        <span class="hljs-keyword">emit</span> NewMemo(
            <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>,
            _name,
            _message
        );
    }

    <span class="hljs-comment">/**
     * @dev send the entire balance stored in this contract to the owner
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdrawTips</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(owner.<span class="hljs-built_in">send</span>(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>));
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/cb945fd71e1196786ff586718195c480732b70186462d60695347ceda29ff84b.png" alt="替换后的合约" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">替换后的合约</figcaption></figure><p>替换后的合约</p><p><strong>4.测试部署合约</strong></p><p>将scripts下面的deploy.js的内容替换为下面的</p><pre data-type="codeBlock" text="const hre = require(&quot;hardhat&quot;);

// Returns the Ether balance of a given address.
async function getBalance(address) {
  const balanceBigInt = await hre.ethers.provider.getBalance(address);
  return hre.ethers.utils.formatEther(balanceBigInt);
}

// Logs the Ether balances for a list of addresses.
async function printBalances(addresses) {
  let idx = 0;
  for (const address of addresses) {
    console.log(`Address ${idx} balance: `, await getBalance(address));
    idx ++;
  }
}

// Logs the memos stored on-chain from coffee purchases.
async function printMemos(memos) {
  for (const memo of memos) {
    const timestamp = memo.timestamp;
    const tipper = memo.name;
    const tipperAddress = memo.from;
    const message = memo.message;
    console.log(`At ${timestamp}, ${tipper} (${tipperAddress}) said: &quot;${message}&quot;`);
  }
}

async function main() {
  // Get the example accounts we&apos;ll be working with.
  const [owner, tipper, tipper2, tipper3] = await hre.ethers.getSigners();

  // We get the contract to deploy.
  const BuyMeACoffee = await hre.ethers.getContractFactory(&quot;BuyMeACoffee&quot;);
  const buyMeACoffee = await BuyMeACoffee.deploy();

  // Deploy the contract.
  await buyMeACoffee.deployed();
  console.log(&quot;BuyMeACoffee deployed to:&quot;, buyMeACoffee.address);

  // Check balances before the coffee purchase.
  const addresses = [owner.address, tipper.address, buyMeACoffee.address];
  console.log(&quot;== start ==&quot;);
  await printBalances(addresses);

  // Buy the owner a few coffees.
  const tip = {value: hre.ethers.utils.parseEther(&quot;1&quot;)};
  await buyMeACoffee.connect(tipper).buyCoffee(&quot;Carolina&quot;, &quot;You&apos;re the best!&quot;, tip);
  await buyMeACoffee.connect(tipper2).buyCoffee(&quot;Vitto&quot;, &quot;Amazing teacher&quot;, tip);
  await buyMeACoffee.connect(tipper3).buyCoffee(&quot;Kay&quot;, &quot;I love my Proof of Knowledge&quot;, tip);

  // Check balances after the coffee purchase.
  console.log(&quot;== bought coffee ==&quot;);
  await printBalances(addresses);

  // Withdraw.
  await buyMeACoffee.connect(owner).withdrawTips();

  // Check balances after withdrawal.
  console.log(&quot;== withdrawTips ==&quot;);
  await printBalances(addresses);

  // Check out the memos.
  console.log(&quot;== memos ==&quot;);
  const memos = await buyMeACoffee.getMemos();
  printMemos(memos);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
  .then(() =&gt; process.exit(0))
  .catch((error) =&gt; {
    console.error(error);
    process.exit(1);
  });
"><code>const hre <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

<span class="hljs-comment">// Returns the Ether balance of a given address.</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBalance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
  const balanceBigInt <span class="hljs-operator">=</span> await hre.ethers.provider.getBalance(<span class="hljs-keyword">address</span>);
  <span class="hljs-keyword">return</span> hre.ethers.utils.formatEther(balanceBigInt);
}

<span class="hljs-comment">// Logs the Ether balances for a list of addresses.</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">printBalances</span>(<span class="hljs-params">addresses</span>) </span>{
  let idx <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
  <span class="hljs-keyword">for</span> (const <span class="hljs-keyword">address</span> of addresses) {
    console.log(`Address ${idx} balance: `, await getBalance(<span class="hljs-keyword">address</span>));
    idx <span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
  }
}

<span class="hljs-comment">// Logs the memos stored on-chain from coffee purchases.</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">printMemos</span>(<span class="hljs-params">memos</span>) </span>{
  <span class="hljs-keyword">for</span> (const memo of memos) {
    const timestamp <span class="hljs-operator">=</span> memo.timestamp;
    const tipper <span class="hljs-operator">=</span> memo.<span class="hljs-built_in">name</span>;
    const tipperAddress <span class="hljs-operator">=</span> memo.from;
    const message <span class="hljs-operator">=</span> memo.message;
    console.log(`At ${timestamp}, ${tipper} (${tipperAddress}) said: <span class="hljs-string">"${message}"</span>`);
  }
}

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Get the example accounts we'll be working with.</span>
  const [owner, tipper, tipper2, tipper3] <span class="hljs-operator">=</span> await hre.ethers.getSigners();

  <span class="hljs-comment">// We get the contract to deploy.</span>
  const BuyMeACoffee <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(<span class="hljs-string">"BuyMeACoffee"</span>);
  const buyMeACoffee <span class="hljs-operator">=</span> await BuyMeACoffee.deploy();

  <span class="hljs-comment">// Deploy the contract.</span>
  await buyMeACoffee.deployed();
  console.log(<span class="hljs-string">"BuyMeACoffee deployed to:"</span>, buyMeACoffee.<span class="hljs-built_in">address</span>);

  <span class="hljs-comment">// Check balances before the coffee purchase.</span>
  const addresses <span class="hljs-operator">=</span> [owner.<span class="hljs-built_in">address</span>, tipper.<span class="hljs-built_in">address</span>, buyMeACoffee.<span class="hljs-built_in">address</span>];
  console.log(<span class="hljs-string">"== start =="</span>);
  await printBalances(addresses);

  <span class="hljs-comment">// Buy the owner a few coffees.</span>
  const tip <span class="hljs-operator">=</span> {<span class="hljs-built_in">value</span>: hre.ethers.utils.parseEther(<span class="hljs-string">"1"</span>)};
  await buyMeACoffee.connect(tipper).buyCoffee(<span class="hljs-string">"Carolina"</span>, <span class="hljs-string">"You're the best!"</span>, tip);
  await buyMeACoffee.connect(tipper2).buyCoffee(<span class="hljs-string">"Vitto"</span>, <span class="hljs-string">"Amazing teacher"</span>, tip);
  await buyMeACoffee.connect(tipper3).buyCoffee(<span class="hljs-string">"Kay"</span>, <span class="hljs-string">"I love my Proof of Knowledge"</span>, tip);

  <span class="hljs-comment">// Check balances after the coffee purchase.</span>
  console.log(<span class="hljs-string">"== bought coffee =="</span>);
  await printBalances(addresses);

  <span class="hljs-comment">// Withdraw.</span>
  await buyMeACoffee.connect(owner).withdrawTips();

  <span class="hljs-comment">// Check balances after withdrawal.</span>
  console.log(<span class="hljs-string">"== withdrawTips =="</span>);
  await printBalances(addresses);

  <span class="hljs-comment">// Check out the memos.</span>
  console.log(<span class="hljs-string">"== memos =="</span>);
  const memos <span class="hljs-operator">=</span> await buyMeACoffee.getMemos();
  printMemos(memos);
}

<span class="hljs-comment">// We recommend this pattern to be able to use async/await everywhere</span>
<span class="hljs-comment">// and properly handle errors.</span>
main()
  .then(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> process.exit(<span class="hljs-number">0</span>))
  .catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
    console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    process.exit(<span class="hljs-number">1</span>);
  });
</code></pre><p>当我们将上面的替换完毕后，通过命令行运行JS</p><pre data-type="codeBlock" text="npx hardhat run scripts/deploy.js
"><code>npx hardhat run scripts<span class="hljs-operator">/</span>deploy.js
</code></pre><p>测试我们的合约，当你执行成功会有下面的现实</p><pre data-type="codeBlock" text="BuyMeACoffee deployed to: 0x90f64aABA6f526219ff9B8DB40854740E7F3804a
== start ==
Address 0 balance:  9999.998754619375
Address 1 balance:  10000.0
Address 2 balance:  0.0
== bought coffee ==
Address 0 balance:  9999.998754619375
Address 1 balance:  9998.999752893990255063
Address 2 balance:  3.0
== withdrawTips ==
Address 0 balance:  10002.998708719732606388
Address 1 balance:  9998.999752893990255063
Address 2 balance:  0.0
== memos ==
At 1660268820, Carolina (0x70997970C51812dc3A010C7d01b50e0d17dc79C8) said: &quot;You&apos;re the best!&quot;
At 1660268821, Vitto (0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC) said: &quot;Amazing teacher&quot;
At 1660268822, Kay (0x90F79bf6EB2c4f870365E785982E1f101E93b906) said: &quot;I love my Proof of Knowledge&quot;
"><code>BuyMeACoffee deployed to: <span class="hljs-number">0x90f64aABA6f526219ff9B8DB40854740E7F3804a</span>
<span class="hljs-operator">=</span><span class="hljs-operator">=</span> start <span class="hljs-operator">=</span><span class="hljs-operator">=</span>
Address <span class="hljs-number">0</span> balance:  <span class="hljs-number">9999.998754619375</span>
Address <span class="hljs-number">1</span> balance:  <span class="hljs-number">10000.0</span>
Address <span class="hljs-number">2</span> balance:  <span class="hljs-number">0</span><span class="hljs-number">.0</span>
<span class="hljs-operator">=</span><span class="hljs-operator">=</span> bought coffee <span class="hljs-operator">=</span><span class="hljs-operator">=</span>
Address <span class="hljs-number">0</span> balance:  <span class="hljs-number">9999.998754619375</span>
Address <span class="hljs-number">1</span> balance:  <span class="hljs-number">9998.999752893990255063</span>
Address <span class="hljs-number">2</span> balance:  <span class="hljs-number">3.0</span>
<span class="hljs-operator">=</span><span class="hljs-operator">=</span> withdrawTips <span class="hljs-operator">=</span><span class="hljs-operator">=</span>
Address <span class="hljs-number">0</span> balance:  <span class="hljs-number">10002.998708719732606388</span>
Address <span class="hljs-number">1</span> balance:  <span class="hljs-number">9998.999752893990255063</span>
Address <span class="hljs-number">2</span> balance:  <span class="hljs-number">0</span><span class="hljs-number">.0</span>
<span class="hljs-operator">=</span><span class="hljs-operator">=</span> memos <span class="hljs-operator">=</span><span class="hljs-operator">=</span>
At <span class="hljs-number">1660268820</span>, Carolina (<span class="hljs-number">0x70997970C51812dc3A010C7d01b50e0d17dc79C8</span>) said: <span class="hljs-string">"You're the best!"</span>
At <span class="hljs-number">1660268821</span>, Vitto (<span class="hljs-number">0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC</span>) said: <span class="hljs-string">"Amazing teacher"</span>
At <span class="hljs-number">1660268822</span>, Kay (<span class="hljs-number">0x90F79bf6EB2c4f870365E785982E1f101E93b906</span>) said: <span class="hljs-string">"I love my Proof of Knowledge"</span>
</code></pre><p><strong>5.使用 Alchemy 和 MetaMask 将 BuyMeACoffe.sol 智能合约部署到以太坊 Goerli 测试网</strong></p><p>新建一个deploy01.js，内容如下：</p><pre data-type="codeBlock" text="// scripts/deploy01.js

const hre = require(&quot;hardhat&quot;);

async function main() {
  // We get the contract to deploy.
  const BuyMeACoffee = await hre.ethers.getContractFactory(&quot;BuyMeACoffee&quot;);
  const buyMeACoffee = await BuyMeACoffee.deploy();

  await buyMeACoffee.deployed();

  console.log(&quot;BuyMeACoffee deployed to:&quot;, buyMeACoffee.address);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
  .then(() =&gt; process.exit(0))
  .catch((error) =&gt; {
    console.error(error);
    process.exit(1);
  });
"><code><span class="hljs-comment">// scripts/deploy01.js</span>

const hre <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// We get the contract to deploy.</span>
  const BuyMeACoffee <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(<span class="hljs-string">"BuyMeACoffee"</span>);
  const buyMeACoffee <span class="hljs-operator">=</span> await BuyMeACoffee.deploy();

  await buyMeACoffee.deployed();

  console.log(<span class="hljs-string">"BuyMeACoffee deployed to:"</span>, buyMeACoffee.<span class="hljs-built_in">address</span>);
}

<span class="hljs-comment">// We recommend this pattern to be able to use async/await everywhere</span>
<span class="hljs-comment">// and properly handle errors.</span>
main()
  .then(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> process.exit(<span class="hljs-number">0</span>))
  .catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
    console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    process.exit(<span class="hljs-number">1</span>);
  });
</code></pre><p>现在我们的项目整体结构就如下了：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0f7e873e76d6b20215d4f121fc167f5306a470677f5a3d18729b38e07903c43a.png" alt="新的项目结构" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">新的项目结构</figcaption></figure><p>新的项目结构</p><p>新的项目结构</p><p>运行我们刚才脚本：</p><pre data-type="codeBlock" text="npx hardhat run scripts/deploy01.js
"><code>npx hardhat run scripts<span class="hljs-operator">/</span>deploy01.js
</code></pre><p>如果成功的话，你会看到下面这样的提示：</p><pre data-type="codeBlock" text="BuyMeACoffee deployed to: 0x90f64aABA6f526219ff9B8DB40854740E7F3804a
"><code>BuyMeACoffee deployed <span class="hljs-selector-tag">to</span>: <span class="hljs-number">0</span>x90f64aABA6f526219ff9B8DB40854740E7F3804a
</code></pre><p>这里需要注意哦，当我们多次运行的话，你每次都会看到完全相同的部署地址，这是因为当你运行脚本时，Hardhat 工具使用的默认设置是本地开发网络，就在您的计算机上。它快速且具有确定性，非常适合进行一些快速的健全性检查。</p><p>但是，为了实际部署到在 Internet 上运行且节点遍布世界各地的测试网络，我们需要更改我们的 Hardhat 配置文件以提供选项。</p><p><strong>6.修改hardhat.config.js 进行配置部署</strong></p><p>首先我们将hardhat.config.js先行修改如下：</p><pre data-type="codeBlock" text="// hardhat.config.js

require(&quot;@nomiclabs/hardhat-ethers&quot;);
require(&quot;@nomiclabs/hardhat-waffle&quot;);
require(&quot;dotenv&quot;).config()

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

const GOERLI_URL = process.env.GOERLI_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;

/**
 * @type import(&apos;hardhat/config&apos;).HardhatUserConfig
 */
module.exports = {
  solidity: &quot;0.8.4&quot;,
  networks: {
    goerli: {
      url: GOERLI_URL,
      accounts: [PRIVATE_KEY]
    }
  }
};
"><code><span class="hljs-comment">// hardhat.config.js</span>

<span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomiclabs/hardhat-ethers"</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomiclabs/hardhat-waffle"</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config()

<span class="hljs-comment">// You need to export an object to set up your config</span>
<span class="hljs-comment">// Go to https://hardhat.org/config/ to learn more</span>

const GOERLI_URL <span class="hljs-operator">=</span> process.env.GOERLI_URL;
const PRIVATE_KEY <span class="hljs-operator">=</span> process.env.PRIVATE_KEY;

<span class="hljs-comment">/**
 * @type import('hardhat/config').HardhatUserConfig
 */</span>
module.exports <span class="hljs-operator">=</span> {
  solidity: <span class="hljs-string">"0.8.4"</span>,
  networks: {
    goerli: {
      url: GOERLI_URL,
      accounts: [PRIVATE_KEY]
    }
  }
};
</code></pre><pre data-type="codeBlock" text="# 安装dotenv
npm install dotenv
# 创建一个.env文件
touch .env
# 将下面内容写入env中
GOERLI_URL=https://eth-goerli.alchemyapi.io/v2/&lt;your api key&gt;
GOERLI_API_KEY=&lt;your api key&gt;
PRIVATE_KEY=&lt;your metamask api key&gt;
"><code># 安装dotenv
npm install dotenv
# 创建一个.env文件
touch .env
# 将下面内容写入env中
GOERLI_URL<span class="hljs-operator">=</span>https:<span class="hljs-comment">//eth-goerli.alchemyapi.io/v2/&#x3C;your api key></span>
GOERLI_API_KEY<span class="hljs-operator">=</span><span class="hljs-operator">&#x3C;</span>your api key<span class="hljs-operator">></span>
PRIVATE_KEY<span class="hljs-operator">=</span><span class="hljs-operator">&#x3C;</span>your metamask api key<span class="hljs-operator">></span>
</code></pre><p>此外，为了获得所需的环境变量，可以使用以下资源：</p><ul><li><p><code>GOERLI_URL</code>- 注册一个帐户<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://alchemy.com/?a=roadtoweb3weektwo">炼金术</a>，创建一个 Ethereum -&gt; Goerli 应用程序，并使用 HTTP URL</p></li><li><p><code>GOERLI_API_KEY</code>- 从您的同一个 Alchemy Ethereum Goerli 应用程序中，您可以获得 URL 的最后一部分，这将是您的 API KEY</p></li><li><p><code>PRIVATE_KEY</code>- 遵循这些<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key">来自 MetaMask 的说明</a>导出您的私钥。</p></li></ul><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/16b1197df1a304b11414bc753a133396919877b8e0771c21598e55ea3dc7dfa6.png" alt="项目新的结构" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">项目新的结构</figcaption></figure><p>项目新的结构</p><p>项目新的结构</p><p>获取Goerli测试币，去下面网址获取测试币</p><p>运行脚本发布到测试网络</p><pre data-type="codeBlock" text="npx hardhat run scripts/deploy01.js --network goerli
"><code>npx hardhat run scripts<span class="hljs-operator">/</span>deploy01.js <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network goerli
</code></pre><p>当你成功部署页面会显示：</p><pre data-type="codeBlock" text=":BuyMeACoffee-contracts paul$ npx hardhat run scripts/deploy01.js --network goerli
Compiled 1 Solidity file successfully
BuyMeACoffee deployed to: 0x90f64aABA6f526219ff9B8DB40854740E7F3804a
"><code>:BuyMeACoffee<span class="hljs-operator">-</span>contracts paul$ npx hardhat run scripts<span class="hljs-operator">/</span>deploy01.js <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network goerli
Compiled <span class="hljs-number">1</span> Solidity file successfully
BuyMeACoffee deployed to: <span class="hljs-number">0x90f64aABA6f526219ff9B8DB40854740E7F3804a</span>
</code></pre><p>验证下合约是否部署上测试网成功。</p><p><strong>7.实现一个脚本</strong><code>withdraw</code></p><p>我们在刚才的脚本下面新建一个withdraw.js的脚本，内容如下：</p><pre data-type="codeBlock" text="// scripts/withdraw.js

const hre = require(&quot;hardhat&quot;);
const abi = require(&quot;../artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json&quot;);

async function getBalance(provider, address) {
  const balanceBigInt = await provider.getBalance(address);
  return hre.ethers.utils.formatEther(balanceBigInt);
}

async function main() {
  // Get the contract that has been deployed to Goerli.
  const contractAddress=&quot;你的合约地址&quot;;
  const contractABI = abi.abi;

  // Get the node connection and wallet connection.
  const provider = new hre.ethers.providers.AlchemyProvider(&quot;goerli&quot;, process.env.GOERLI_API_KEY);

  // Ensure that signer is the SAME address as the original contract deployer,
  // or else this script will fail with an error.
  const signer = new hre.ethers.Wallet(process.env.PRIVATE_KEY, provider);

  // Instantiate connected contract.
  const buyMeACoffee = new hre.ethers.Contract(contractAddress, contractABI, signer);

  // Check starting balances.
  console.log(&quot;current balance of owner: &quot;, await getBalance(provider, signer.address), &quot;ETH&quot;);
  const contractBalance = await getBalance(provider, buyMeACoffee.address);
  console.log(&quot;current balance of contract: &quot;, await getBalance(provider, buyMeACoffee.address), &quot;ETH&quot;);

  // Withdraw funds if there are funds to withdraw.
  if (contractBalance !== &quot;0.0&quot;) {
    console.log(&quot;withdrawing funds..&quot;)
    const withdrawTxn = await buyMeACoffee.withdrawTips();
    await withdrawTxn.wait();
  } else {
    console.log(&quot;no funds to withdraw!&quot;);
  }

  // Check ending balance.
  console.log(&quot;current balance of owner: &quot;, await getBalance(provider, signer.address), &quot;ETH&quot;);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
  .then(() =&gt; process.exit(0))
  .catch((error) =&gt; {
    console.error(error);
    process.exit(1);
  });
"><code><span class="hljs-comment">// scripts/withdraw.js</span>

const hre <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);
const <span class="hljs-built_in">abi</span> <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"../artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBalance</span>(<span class="hljs-params">provider, <span class="hljs-keyword">address</span></span>) </span>{
  const balanceBigInt <span class="hljs-operator">=</span> await provider.getBalance(<span class="hljs-keyword">address</span>);
  <span class="hljs-keyword">return</span> hre.ethers.utils.formatEther(balanceBigInt);
}

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Get the contract that has been deployed to Goerli.</span>
  const contractAddress<span class="hljs-operator">=</span><span class="hljs-string">"你的合约地址"</span>;
  const contractABI <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">abi</span>;

  <span class="hljs-comment">// Get the node connection and wallet connection.</span>
  const provider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> hre.ethers.providers.AlchemyProvider(<span class="hljs-string">"goerli"</span>, process.env.GOERLI_API_KEY);

  <span class="hljs-comment">// Ensure that signer is the SAME address as the original contract deployer,</span>
  <span class="hljs-comment">// or else this script will fail with an error.</span>
  const signer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> hre.ethers.Wallet(process.env.PRIVATE_KEY, provider);

  <span class="hljs-comment">// Instantiate connected contract.</span>
  const buyMeACoffee <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> hre.ethers.Contract(contractAddress, contractABI, signer);

  <span class="hljs-comment">// Check starting balances.</span>
  console.log(<span class="hljs-string">"current balance of owner: "</span>, await getBalance(provider, signer.<span class="hljs-built_in">address</span>), <span class="hljs-string">"ETH"</span>);
  const contractBalance <span class="hljs-operator">=</span> await getBalance(provider, buyMeACoffee.<span class="hljs-built_in">address</span>);
  console.log(<span class="hljs-string">"current balance of contract: "</span>, await getBalance(provider, buyMeACoffee.<span class="hljs-built_in">address</span>), <span class="hljs-string">"ETH"</span>);

  <span class="hljs-comment">// Withdraw funds if there are funds to withdraw.</span>
  <span class="hljs-keyword">if</span> (contractBalance <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"0.0"</span>) {
    console.log(<span class="hljs-string">"withdrawing funds.."</span>)
    const withdrawTxn <span class="hljs-operator">=</span> await buyMeACoffee.withdrawTips();
    await withdrawTxn.wait();
  } <span class="hljs-keyword">else</span> {
    console.log(<span class="hljs-string">"no funds to withdraw!"</span>);
  }

  <span class="hljs-comment">// Check ending balance.</span>
  console.log(<span class="hljs-string">"current balance of owner: "</span>, await getBalance(provider, signer.<span class="hljs-built_in">address</span>), <span class="hljs-string">"ETH"</span>);
}

<span class="hljs-comment">// We recommend this pattern to be able to use async/await everywhere</span>
<span class="hljs-comment">// and properly handle errors.</span>
main()
  .then(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> process.exit(<span class="hljs-number">0</span>))
  .catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
    console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    process.exit(<span class="hljs-number">1</span>);
  });
</code></pre><p>在<strong>本地</strong>测试</p><pre data-type="codeBlock" text="# 运行该命令在本地测试
npx hardhat run scripts/withdraw.js
"><code># 运行该命令在本地测试
npx hardhat run scripts<span class="hljs-operator">/</span>withdraw.js
</code></pre><p>如果没有报错你会有下面的提示：</p><pre data-type="codeBlock" text="current balance of owner:  0.14511094798885063 ETH
current balance of contract:  0.0 ETH
no funds to withdraw!
current balance of owner:  0.14511094798885063 ETH
"><code><span class="hljs-attr">current balance of owner:</span>  <span class="hljs-number">0.14511094798885063</span> <span class="hljs-string">ETH</span>
<span class="hljs-attr">current balance of contract:</span>  <span class="hljs-number">0.0</span> <span class="hljs-string">ETH</span>
<span class="hljs-literal">no</span> <span class="hljs-string">funds</span> <span class="hljs-string">to</span> <span class="hljs-string">withdraw!</span>
<span class="hljs-attr">current balance of owner:</span>  <span class="hljs-number">0.14511094798885063</span> <span class="hljs-string">ETH</span>
</code></pre><p><strong>8.使用 Replit 和 Ethers.js 构建前端 Buy Me A Coffee 网站 dapp</strong></p><p>首先在Replit IDE将下面的仓库进行fork</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2655963a2d3b5116ac6270995d38a4d466f77c9da7081ed32d38568511b8422a.png" alt="fork" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">fork</figcaption></figure><p>fork</p><p>fork</p><p>在fork之后我们会来到自己的工作台，如下所示：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0ce8dba7dcb7cc1da111e9a24f9422b9405b20aff2514885d5174c551336e7c0.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>将文件中写好的变量进行修改</p><ul><li><p>更新输入<code>contractAddresspages/index.js</code></p></li><li><p>将名称字符串更新为您自己的名字<code>pages/index.js</code></p></li><li><p>确保合同 ABI 与您的合同相匹配<code>utils/BuyMeACoffee.json</code></p></li></ul><p>可以看到 contractAddress 变量已经填充了地址。修改成你自己部署的合约</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f2c4da8f2224061338f79ddc36d865422f43453dc3173125bbe6da3bf540de91.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>将复制过来的仓库的Albert修改成你想要的任意名字都可以</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5b7a7b43753bf99d252396cb9d7a3ea0b513a0d13ce5f918905d1fb3d0beadeb.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>将刚才在编译器生成的ABI复制到Replit中的utils/BuyMeACoffee.json</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/00a5001493cb5898ce2ffcbcb767a3737da52610432420d966797654d53e4dd3.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/61642e001ec23929f19756724499261a5887475a5d5f601027d3430bbd03366d.png" alt="运行项目" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">运行项目</figcaption></figure><p>运行项目</p><pre data-type="codeBlock" text="
"><code></code></pre>]]></content:encoded>
            <author>monkey-11@newsletter.paragraph.com (Monkey)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3第十周教程- 使用 Lens 协议创建去中心化 Twitter]]></title>
            <link>https://paragraph.com/@monkey-11/alchemy-the-road-to-web3-lens-twitter</link>
            <guid>Wgo4tjnAd6CkMypkQtbP</guid>
            <pubDate>Sun, 25 Sep 2022 15:22:16 GMT</pubDate>
            <description><![CDATA[在本课中，将学习：如何使用 Apollo GraphQL 客户端设置 Next.js 应用程序如何使用 Lens 协议 API 获取存储在 Polygon 区块链上的个人资料、帖子和其他数据MintKudos API 简介——以便您可以将您的 PoK 代币集成到您的 dapp 中！Lit 协议简介——如果您想加密某些帖子以仅显示给各个社区成员如何使用 Repl.it 部署你的去中心化社交媒体应用程序前端网站扩展此项目的多个挑战选项！话不多说了，我们开始今天的课程吧。1.设置依赖安装Apollo我们今天的课程需要在VScode中去执行，首先我们需要建立一个项目# 创建一个road-to-lens 这是注释 不要输入命令行 npx create-next-app road-to-lens # 安装graphql npm install @apollo/client graphql # 运行项目验证 npm run dev 当你出现这样的结果，恭喜你已经成功完成第一步了。2.在 index.js 页面上使用 Lens 推荐的配置文件尝试 Apollo GraphQL2.1新建apoll...]]></description>
            <content:encoded><![CDATA[<p>在本课中，将学习：</p><ul><li><p>如何使用 Apollo GraphQL 客户端设置 Next.js 应用程序</p></li><li><p>如何使用 Lens 协议 API 获取存储在 Polygon 区块链上的个人资料、帖子和其他数据</p></li><li><p>MintKudos API 简介——以便您可以将您的 PoK 代币集成到您的 dapp 中！</p></li><li><p>Lit 协议简介——如果您想加密某些帖子以仅显示给各个社区成员</p></li><li><p>如何使用 Repl.it 部署你的去中心化社交媒体应用程序前端网站</p></li><li><p>扩展此项目的多个挑战选项！</p></li></ul><p>话不多说了，我们开始今天的课程吧。</p><h2 id="h-1apollo" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1.设置依赖安装Apollo</h2><p>我们今天的课程需要在VScode中去执行，首先我们需要建立一个项目</p><pre data-type="codeBlock" text="# 创建一个road-to-lens 这是注释 不要输入命令行
npx create-next-app road-to-lens
# 安装graphql
npm install @apollo/client graphql
# 运行项目验证
npm run dev
"><code># 创建一个road<span class="hljs-operator">-</span>to<span class="hljs-operator">-</span>lens 这是注释 不要输入命令行
npx create<span class="hljs-operator">-</span>next<span class="hljs-operator">-</span>app road<span class="hljs-operator">-</span>to<span class="hljs-operator">-</span>lens
# 安装graphql
npm install @apollo<span class="hljs-operator">/</span>client graphql
# 运行项目验证
npm run dev
</code></pre><p>当你出现这样的结果，恭喜你已经成功完成第一步了。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f2e6d03237bf7af023c18b697aeeaed092c0c70c5e5e40acc5b94007e508ee6e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-2-indexjs-lens-apollo-graphql" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.在 index.js 页面上使用 Lens 推荐的配置文件尝试 Apollo GraphQL</h2><h3 id="h-21apollo-clientjs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.1新建apollo-client.js</h3><p>我们在当前项目新建一个apollo-client.js输入下面的内容：</p><pre data-type="codeBlock" text="// ./apollo-client.js

import { ApolloClient, InMemoryCache } from &quot;@apollo/client&quot;;

const client = new ApolloClient({
    uri: &quot;https://api.lens.dev&quot;,
    cache: new InMemoryCache(),
});

export default client;
"><code><span class="hljs-comment">// ./apollo-client.js</span>

<span class="hljs-keyword">import</span> { <span class="hljs-title class_">ApolloClient</span>, <span class="hljs-title class_">InMemoryCache</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"@apollo/client"</span>;

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ApolloClient</span>({
    <span class="hljs-attr">uri</span>: <span class="hljs-string">"https://api.lens.dev"</span>,
    <span class="hljs-attr">cache</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">InMemoryCache</span>(),
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> client;
</code></pre><p>修改我们的/pages/_app.js：</p><pre data-type="codeBlock" text="// pages/_app.js

import &apos;../styles/globals.css&apos;
import { ApolloProvider } from &quot;@apollo/client&quot;;
import client from &quot;../apollo-client&quot;;

function MyApp({ Component, pageProps }) {
  return (
    &lt;ApolloProvider client={client}&gt;
      &lt;Component {...pageProps} /&gt;
    &lt;/ApolloProvider&gt;
  );
}

export default MyApp
"><code><span class="hljs-comment">// pages/_app.js</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'../styles/globals.css'</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> { <span class="hljs-title">ApolloProvider</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@apollo/client"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">client</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../apollo-client"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyApp</span>(<span class="hljs-params">{ Component, pageProps }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>ApolloProvider client<span class="hljs-operator">=</span>{client}<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>Component {...pageProps} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>ApolloProvider<span class="hljs-operator">></span>
  );
}

export default MyApp
</code></pre><p>修改/pages/index.js</p><pre data-type="codeBlock" text="import { useQuery } from &quot;@apollo/client&quot;;
import recommendedProfilesQuery from &apos;../queries/recommendedProfilesQuery.js&apos;;
import Profile from &apos;../components/Profile.js&apos;;

export default function Home() {
  const {loading, error, data} = useQuery(recommendedProfilesQuery);


  if (loading) return &apos;Loading..&apos;;
  if (error) return `Error! ${error.message}`;

  return (
    &lt;div&gt;
      {data.recommendedProfiles.map((profile, index) =&gt; {
        console.log(`Profile ${index}:`, profile);
        return &lt;Profile key={profile.id} profile={profile} displayFullProfile={false} /&gt;;
      })}
    &lt;/div&gt;
  )
}
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">useQuery</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@apollo/client"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">recommendedProfilesQuery</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'../queries/recommendedProfilesQuery.js'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Profile</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'../components/Profile.js'</span>;

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  const {loading, <span class="hljs-function"><span class="hljs-keyword">error</span>, <span class="hljs-title">data</span>} = <span class="hljs-title">useQuery</span>(<span class="hljs-params">recommendedProfilesQuery</span>)</span>;


  <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="hljs-string">'Loading..'</span>;
  <span class="hljs-keyword">if</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) <span class="hljs-title"><span class="hljs-keyword">return</span></span> `<span class="hljs-title"><span class="hljs-built_in">Error</span></span>! <span class="hljs-title">$</span></span>{<span class="hljs-keyword">error</span>.message}`;

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
      {data.recommendedProfiles.map((profile, index) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
        console.log(`Profile ${index}:`, profile);
        <span class="hljs-keyword">return</span> <span class="hljs-operator">&#x3C;</span>Profile key<span class="hljs-operator">=</span>{profile.id} profile<span class="hljs-operator">=</span>{profile} displayFullProfile<span class="hljs-operator">=</span>{<span class="hljs-literal">false</span>} <span class="hljs-operator">/</span><span class="hljs-operator">></span>;
      })}
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  )
}
</code></pre><p>新建查询js</p><pre data-type="codeBlock" text="mkdir queries
touch queries/recommendedProfilesQuery.js
"><code><span class="hljs-built_in">mkdir</span> queries
<span class="hljs-built_in">touch</span> queries/recommendedProfilesQuery.js
</code></pre><p>将下面的代码写入recommendedProfilesQuery.js</p><pre data-type="codeBlock" text="// queries/recommendedProfilesQuery.js

import {gql} from &apos;@apollo/client&apos;;

export default gql`
  query RecommendedProfiles {
    recommendedProfiles {
          id
        name
        bio
        attributes {
          displayType
          traitType
          key
          value
        }
          followNftAddress
        metadata
        isDefault
        picture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        handle
        coverPicture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        ownedBy
        dispatcher {
          address
          canUseRelay
        }
        stats {
          totalFollowers
          totalFollowing
          totalPosts
          totalComments
          totalMirrors
          totalPublications
          totalCollects
        }
        followModule {
          ... on FeeFollowModuleSettings {
            type
            amount {
              asset {
                symbol
                name
                decimals
                address
              }
              value
            }
            recipient
          }
          ... on ProfileFollowModuleSettings {
          type
          }
          ... on RevertFollowModuleSettings {
          type
          }
        }
    }
  }
`;
"><code><span class="hljs-comment">// queries/recommendedProfilesQuery.js</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">gql</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@apollo/client'</span>;

export default gql`
  query RecommendedProfiles {
    recommendedProfiles {
          id
        name
        bio
        attributes {
          displayType
          traitType
          key
          value
        }
          followNftAddress
        metadata
        isDefault
        picture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        handle
        coverPicture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        ownedBy
        dispatcher {
          <span class="hljs-keyword">address</span>
          canUseRelay
        }
        stats {
          totalFollowers
          totalFollowing
          totalPosts
          totalComments
          totalMirrors
          totalPublications
          totalCollects
        }
        followModule {
          ... on FeeFollowModuleSettings {
            <span class="hljs-keyword">type</span>
            amount {
              asset {
                symbol
                name
                decimals
                <span class="hljs-keyword">address</span>
              }
              value
            }
            recipient
          }
          ... on ProfileFollowModuleSettings {
          <span class="hljs-keyword">type</span>
          }
          ... on RevertFollowModuleSettings {
          <span class="hljs-keyword">type</span>
          }
        }
    }
  }
`;
</code></pre><p>新建组件js</p><pre data-type="codeBlock" text="mkdir components
touch components/Profile.js
"><code>mkdir components
touch components<span class="hljs-operator">/</span>Profile.js
</code></pre><p>将下面代码写入Profile.js</p><pre data-type="codeBlock" text="// components/Profile.js

import Link from &quot;next/link&quot;;
export default function Profile(props) {
  const profile = props.profile;

  // When displayFullProfile is true, we show more info.
  const displayFullProfile = props.displayFullProfile;

  return (
    &lt;div className=&quot;p-8&quot;&gt;
      &lt;Link href={`/profile/${profile.id}`}&gt;
        &lt;div className=&quot;max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl&quot;&gt;
          &lt;div className=&quot;md:flex&quot;&gt;
            &lt;div className=&quot;md:shrink-0&quot;&gt;
              {profile.picture ? (
                &lt;img
                  src={
                    profile.picture.original
                      ? profile.picture.original.url
                      : profile.picture.uri
                  }
                  className=&quot;h-48 w-full object-cover md:h-full md:w-48&quot;
                /&gt;
              ) : (
                &lt;div
                  style={{
                    backgrondColor: &quot;gray&quot;,
                  }}
                  className=&quot;h-48 w-full object-cover md:h-full md:w-48&quot;
                /&gt;
              )}
            &lt;/div&gt;
            &lt;div className=&quot;p-8&quot;&gt;
              &lt;div className=&quot;uppercase tracking-wide text-sm text-indigo-500 font-semibold&quot;&gt;
                {profile.handle}
                {displayFullProfile &amp;&amp;
                  profile.name &amp;&amp;
                  &quot; (&quot; + profile.name + &quot;)&quot;}
              &lt;/div&gt;
              &lt;div className=&quot;block mt-1 text-sm leading-tight font-medium text-black hover:underline&quot;&gt;
                {profile.bio}
              &lt;/div&gt;
              &lt;div className=&quot;mt-2 text-sm text-slate-900&quot;&gt;{profile.ownedBy}&lt;/div&gt;
              &lt;p className=&quot;mt-2 text-xs text-slate-500&quot;&gt;
                following: {profile.stats.totalFollowing} followers:{&quot; &quot;}
                {profile.stats.totalFollowers}
              &lt;/p&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/Link&gt;
    &lt;/div&gt;
  );
}
"><code><span class="hljs-comment">// components/Profile.js</span>

<span class="hljs-keyword">import</span> <span class="hljs-title">Link</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"next/link"</span>;
export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Profile</span>(<span class="hljs-params">props</span>) </span>{
  const profile <span class="hljs-operator">=</span> props.profile;

  <span class="hljs-comment">// When displayFullProfile is true, we show more info.</span>
  const displayFullProfile <span class="hljs-operator">=</span> props.displayFullProfile;

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"p-8"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>Link href<span class="hljs-operator">=</span>{`<span class="hljs-operator">/</span>profile<span class="hljs-operator">/</span>${profile.id}`}<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl"</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"md:flex"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"md:shrink-0"</span><span class="hljs-operator">></span>
              {profile.picture ? (
                <span class="hljs-operator">&#x3C;</span>img
                  src<span class="hljs-operator">=</span>{
                    profile.picture.original
                      ? profile.picture.original.url
                      : profile.picture.uri
                  }
                  className<span class="hljs-operator">=</span><span class="hljs-string">"h-48 w-full object-cover md:h-full md:w-48"</span>
                <span class="hljs-operator">/</span><span class="hljs-operator">></span>
              ) : (
                <span class="hljs-operator">&#x3C;</span>div
                  style<span class="hljs-operator">=</span>{{
                    backgrondColor: <span class="hljs-string">"gray"</span>,
                  }}
                  className<span class="hljs-operator">=</span><span class="hljs-string">"h-48 w-full object-cover md:h-full md:w-48"</span>
                <span class="hljs-operator">/</span><span class="hljs-operator">></span>
              )}
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"p-8"</span><span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"uppercase tracking-wide text-sm text-indigo-500 font-semibold"</span><span class="hljs-operator">></span>
                {profile.handle}
                {displayFullProfile <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
                  profile.<span class="hljs-built_in">name</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
                  <span class="hljs-string">" ("</span> <span class="hljs-operator">+</span> profile.<span class="hljs-built_in">name</span> <span class="hljs-operator">+</span> <span class="hljs-string">")"</span>}
              <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"block mt-1 text-sm leading-tight font-medium text-black hover:underline"</span><span class="hljs-operator">></span>
                {profile.bio}
              <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"mt-2 text-sm text-slate-900"</span><span class="hljs-operator">></span>{profile.ownedBy}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>p className<span class="hljs-operator">=</span><span class="hljs-string">"mt-2 text-xs text-slate-500"</span><span class="hljs-operator">></span>
                following: {profile.stats.totalFollowing} followers:{<span class="hljs-string">" "</span>}
                {profile.stats.totalFollowers}
              <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  );
}
</code></pre><p>我们将项目运行起来出现下面的结果说明你已经离成功不远了，但是现在页面的样式太丑了，让我们来优化下。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/bf4f19eb1467b07d5b089725ce9aeb64dbb97d769cd5a15c44ee8a3affef06ac.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-22" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.2优化页面样式</h3><pre data-type="codeBlock" text="# 安装 Tailwind
npm install -D tailwindcss postcss autoprefixer
# 当你执行完下面这个命令，系统会给你生成一个tailwind.config.js
npx tailwindcss init -p
"><code># 安装 Tailwind
npm install <span class="hljs-operator">-</span>D tailwindcss postcss autoprefixer
# 当你执行完下面这个命令，系统会给你生成一个tailwind.config.js
npx tailwindcss init <span class="hljs-operator">-</span>p
</code></pre><p>在生成的tailwind.config.js中写入一下内容：</p><pre data-type="codeBlock" text="// tailwind.config.js

module.exports = {
  content: [
    &quot;./pages/**/*.{js,ts,jsx,tsx}&quot;,
    &quot;./components/**/*.{js,ts,jsx,tsx}&quot;,
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
"><code>// tailwind<span class="hljs-selector-class">.config</span><span class="hljs-selector-class">.js</span>

module<span class="hljs-selector-class">.exports</span> = {
  <span class="hljs-attribute">content</span>: [
    <span class="hljs-string">"./pages/**/*.{js,ts,jsx,tsx}"</span>,
    <span class="hljs-string">"./components/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
</code></pre><p>同时将我们的globals.css文件末尾加入下面的内容：</p><pre data-type="codeBlock" text="/* ./styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
"><code><span class="hljs-comment">/* ./styles/globals.css */</span>
<span class="hljs-variable">@tailwind</span> base;
<span class="hljs-variable">@tailwind</span> components;
<span class="hljs-variable">@tailwind</span> utilities;
</code></pre><p>最终我们的结果展示如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/645311ec9ba9971e075888c39869d20d57c1b649ec96c2e8fde8f2d3a0276012.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3. 创建个人资料页面</h2><p>首先我们创建个人资料文件夹：</p><pre data-type="codeBlock" text="mkdir pages/profile
road-to-lens % touch pages/profile/\[id\].js
touch queries/fetchProfileQuery.js
"><code>mkdir pages<span class="hljs-operator">/</span>profile
road<span class="hljs-operator">-</span>to<span class="hljs-operator">-</span>lens <span class="hljs-operator">%</span> touch pages<span class="hljs-operator">/</span>profile<span class="hljs-operator">/</span>\[id\].js
touch queries<span class="hljs-operator">/</span>fetchProfileQuery.js
</code></pre><p>在fetchProfileQuery.js中写入下面的代码：</p><pre data-type="codeBlock" text="// queries/fetchProfileQuery.js

import { gql } from &apos;@apollo/client&apos;;

export default gql`
query($request: SingleProfileQueryRequest!) {
    profile(request: $request) {
        id
        name
        bio
        attributes {
          displayType
          traitType
          key
          value
        }
        followNftAddress
        metadata
        isDefault
        picture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        handle
        coverPicture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        ownedBy
        dispatcher {
          address
          canUseRelay
        }
        stats {
          totalFollowers
          totalFollowing
          totalPosts
          totalComments
          totalMirrors
          totalPublications
          totalCollects
        }
        followModule {
          ... on FeeFollowModuleSettings {
            type
            amount {
              asset {
                symbol
                name
                decimals
                address
              }
              value
            }
            recipient
          }
          ... on ProfileFollowModuleSettings {
            type
          }
          ... on RevertFollowModuleSettings {
            type
          }
        }
    }
  }
`;
"><code><span class="hljs-comment">// queries/fetchProfileQuery.js</span>

<span class="hljs-keyword">import</span> { <span class="hljs-title">gql</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@apollo/client'</span>;

export default gql`
query($request: SingleProfileQueryRequest<span class="hljs-operator">!</span>) {
    profile(request: $request) {
        id
        name
        bio
        attributes {
          displayType
          traitType
          key
          value
        }
        followNftAddress
        metadata
        isDefault
        picture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        handle
        coverPicture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        ownedBy
        dispatcher {
          <span class="hljs-keyword">address</span>
          canUseRelay
        }
        stats {
          totalFollowers
          totalFollowing
          totalPosts
          totalComments
          totalMirrors
          totalPublications
          totalCollects
        }
        followModule {
          ... on FeeFollowModuleSettings {
            <span class="hljs-keyword">type</span>
            amount {
              asset {
                symbol
                name
                decimals
                <span class="hljs-keyword">address</span>
              }
              value
            }
            recipient
          }
          ... on ProfileFollowModuleSettings {
            <span class="hljs-keyword">type</span>
          }
          ... on RevertFollowModuleSettings {
            <span class="hljs-keyword">type</span>
          }
        }
    }
  }
`;
</code></pre><p>在id.js文件中写入下面的代码：</p><pre data-type="codeBlock" text="// pages/profile/[id].js

import { useQuery } from &quot;@apollo/client&quot;;
import { useRouter } from &quot;next/router&quot;;
import fetchProfileQuery from &quot;../../queries/fetchProfileQuery.js&quot;;

import Profile from &quot;../../components/Profile.js&quot;;

export default function ProfilePage() {
  const router = useRouter();
  const { id } = router.query;

  console.log(&quot;fetching profile for&quot;, id);
  const { loading, error, data } = useQuery(fetchProfileQuery, {
    variables: { request: { profileId: id } },
  });

  if (loading) return &quot;Loading..&quot;;
  if (error) return `Error! ${error.message}`;

  console.log(&quot;on profile page data: &quot;, data);

  return &lt;Profile profile={data.profile} displayFullProfile={true}/&gt;
}
"><code><span class="hljs-comment">// pages/profile/[id].js</span>

<span class="hljs-keyword">import</span> { <span class="hljs-title">useQuery</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@apollo/client"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useRouter</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"next/router"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">fetchProfileQuery</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../../queries/fetchProfileQuery.js"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-title">Profile</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../../components/Profile.js"</span>;

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ProfilePage</span>(<span class="hljs-params"></span>) </span>{
  const router <span class="hljs-operator">=</span> useRouter();
  const { id } <span class="hljs-operator">=</span> router.query;

  console.log(<span class="hljs-string">"fetching profile for"</span>, id);
  const { loading, <span class="hljs-function"><span class="hljs-keyword">error</span>, <span class="hljs-title">data</span> } = <span class="hljs-title">useQuery</span>(<span class="hljs-params">fetchProfileQuery, {
    variables: { request: { profileId: id } },
  }</span>)</span>;

  <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="hljs-string">"Loading.."</span>;
  <span class="hljs-keyword">if</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) <span class="hljs-title"><span class="hljs-keyword">return</span></span> `<span class="hljs-title"><span class="hljs-built_in">Error</span></span>! <span class="hljs-title">$</span></span>{<span class="hljs-keyword">error</span>.message}`;

  console.log(<span class="hljs-string">"on profile page data: "</span>, data);

  <span class="hljs-keyword">return</span> <span class="hljs-operator">&#x3C;</span>Profile profile<span class="hljs-operator">=</span>{data.profile} displayFullProfile<span class="hljs-operator">=</span>{<span class="hljs-literal">true</span>}<span class="hljs-operator">/</span><span class="hljs-operator">></span>
}
</code></pre><p>当我们上面代码编写完毕后，可以输入下面的链接进行验证</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://localhost:3000/profile/0x9752">http://localhost:3000/profile/0x9752</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://localhost:3000/profile/0x25c4">http://localhost:3000/profile/0x25c4</a></p></li></ul><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e7b5b7ff799e4efc737a047d817f8939409220c465d2a6c03f053171ccb2697b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>如果到目前为止一切顺利的话，你已经完成了一半了。</p><h2 id="h-4" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">4.在个人资料页面上加载用户帖子</h2><p>将fetchProfileQuery.js覆盖为下面的：</p><pre data-type="codeBlock" text="import { gql } from &quot;@apollo/client&quot;;

export default gql`
  query (
    $request: SingleProfileQueryRequest!
    $publicationsRequest: PublicationsQueryRequest!
  ) {
    publications( request: $publicationsRequest) {
      items {
        __typename
        ... on Post {
          ...PostFields
        }
        ... on Comment {
          ...CommentFields
        }
        ... on Mirror {
          ...MirrorFields
        }
      }
      pageInfo {
        prev
        next
        totalCount
      }
    }
    profile(request: $request) {
      id
      name
      bio
      attributes {
        displayType
        traitType
        key
        value
      }
      followNftAddress
      metadata
      isDefault
      picture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            url
            mimeType
          }
        }
        __typename
      }
      handle
      coverPicture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            url
            mimeType
          }
        }
        __typename
      }
      ownedBy
      dispatcher {
        address
        canUseRelay
      }
      stats {
        totalFollowers
        totalFollowing
        totalPosts
        totalComments
        totalMirrors
        totalPublications
        totalCollects
      }
      followModule {
        ... on FeeFollowModuleSettings {
          type
          amount {
            asset {
              symbol
              name
              decimals
              address
            }
            value
          }
          recipient
        }
        ... on ProfileFollowModuleSettings {
          type
        }
        ... on RevertFollowModuleSettings {
          type
        }
      }
    }
  }

  fragment MediaFields on Media {
    url
    mimeType
  }

  fragment ProfileFields on Profile {
    id
    name
    bio
    attributes {
      displayType
      traitType
      key
      value
    }
    isFollowedByMe
    isFollowing(who: null)
    followNftAddress
    metadata
    isDefault
    handle
    picture {
      ... on NftImage {
        contractAddress
        tokenId
        uri
        verified
      }
      ... on MediaSet {
        original {
          ...MediaFields
        }
      }
    }
    coverPicture {
      ... on NftImage {
        contractAddress
        tokenId
        uri
        verified
      }
      ... on MediaSet {
        original {
          ...MediaFields
        }
      }
    }
    ownedBy
    dispatcher {
      address
    }
    stats {
      totalFollowers
      totalFollowing
      totalPosts
      totalComments
      totalMirrors
      totalPublications
      totalCollects
    }
    followModule {
      ... on FeeFollowModuleSettings {
        type
        amount {
          asset {
            name
            symbol
            decimals
            address
          }
          value
        }
        recipient
      }
      ... on ProfileFollowModuleSettings {
        type
      }
      ... on RevertFollowModuleSettings {
        type
      }
    }
  }

  fragment PublicationStatsFields on PublicationStats {
    totalAmountOfMirrors
    totalAmountOfCollects
    totalAmountOfComments
  }

  fragment MetadataOutputFields on MetadataOutput {
    name
    description
    content
    media {
      original {
        ...MediaFields
      }
    }
    attributes {
      displayType
      traitType
      value
    }
  }

  fragment Erc20Fields on Erc20 {
    name
    symbol
    decimals
    address
  }

  fragment CollectModuleFields on CollectModule {
    __typename
    ... on FreeCollectModuleSettings {
      type
      followerOnly
      contractAddress
    }
    ... on FeeCollectModuleSettings {
      type
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
    }
    ... on LimitedFeeCollectModuleSettings {
      type
      collectLimit
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
    }
    ... on LimitedTimedFeeCollectModuleSettings {
      type
      collectLimit
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
      endTimestamp
    }
    ... on RevertCollectModuleSettings {
      type
    }
    ... on TimedFeeCollectModuleSettings {
      type
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
      endTimestamp
    }
  }

  fragment PostFields on Post {
    id
    profile {
      ...ProfileFields
    }
    stats {
      ...PublicationStatsFields
    }
    metadata {
      ...MetadataOutputFields
    }
    createdAt
    collectModule {
      ...CollectModuleFields
    }
    referenceModule {
      ... on FollowOnlyReferenceModuleSettings {
        type
      }
    }
    appId
    hidden
    mirrors(by: null)
    hasCollectedByMe
  }

  fragment MirrorBaseFields on Mirror {
    id
    profile {
      ...ProfileFields
    }
    stats {
      ...PublicationStatsFields
    }
    metadata {
      ...MetadataOutputFields
    }
    createdAt
    collectModule {
      ...CollectModuleFields
    }
    referenceModule {
      ... on FollowOnlyReferenceModuleSettings {
        type
      }
    }
    appId
    hidden
    hasCollectedByMe
  }

  fragment MirrorFields on Mirror {
    ...MirrorBaseFields
    mirrorOf {
      ... on Post {
        ...PostFields
      }
      ... on Comment {
        ...CommentFields
      }
    }
  }

  fragment CommentBaseFields on Comment {
    id
    profile {
      ...ProfileFields
    }
    stats {
      ...PublicationStatsFields
    }
    metadata {
      ...MetadataOutputFields
    }
    createdAt
    collectModule {
      ...CollectModuleFields
    }
    referenceModule {
      ... on FollowOnlyReferenceModuleSettings {
        type
      }
    }
    appId
    hidden
    mirrors(by: null)
    hasCollectedByMe
  }

  fragment CommentFields on Comment {
    ...CommentBaseFields
    mainPost {
      ... on Post {
        ...PostFields
      }
      ... on Mirror {
        ...MirrorBaseFields
        mirrorOf {
          ... on Post {
            ...PostFields
          }
          ... on Comment {
            ...CommentMirrorOfFields
          }
        }
      }
    }
  }

  fragment CommentMirrorOfFields on Comment {
    ...CommentBaseFields
    mainPost {
      ... on Post {
        ...PostFields
      }
      ... on Mirror {
        ...MirrorBaseFields
      }
    }
  }
`;
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">gql</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@apollo/client"</span>;

export default gql`
  query (
    $request: SingleProfileQueryRequest<span class="hljs-operator">!</span>
    $publicationsRequest: PublicationsQueryRequest<span class="hljs-operator">!</span>
  ) {
    publications( request: $publicationsRequest) {
      items {
        __typename
        ... on Post {
          ...PostFields
        }
        ... on Comment {
          ...CommentFields
        }
        ... on Mirror {
          ...MirrorFields
        }
      }
      pageInfo {
        prev
        next
        totalCount
      }
    }
    profile(request: $request) {
      id
      name
      bio
      attributes {
        displayType
        traitType
        key
        value
      }
      followNftAddress
      metadata
      isDefault
      picture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            url
            mimeType
          }
        }
        __typename
      }
      handle
      coverPicture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            url
            mimeType
          }
        }
        __typename
      }
      ownedBy
      dispatcher {
        <span class="hljs-keyword">address</span>
        canUseRelay
      }
      stats {
        totalFollowers
        totalFollowing
        totalPosts
        totalComments
        totalMirrors
        totalPublications
        totalCollects
      }
      followModule {
        ... on FeeFollowModuleSettings {
          <span class="hljs-keyword">type</span>
          amount {
            asset {
              symbol
              name
              decimals
              <span class="hljs-keyword">address</span>
            }
            value
          }
          recipient
        }
        ... on ProfileFollowModuleSettings {
          <span class="hljs-keyword">type</span>
        }
        ... on RevertFollowModuleSettings {
          <span class="hljs-keyword">type</span>
        }
      }
    }
  }

  fragment MediaFields on Media {
    url
    mimeType
  }

  fragment ProfileFields on Profile {
    id
    name
    bio
    attributes {
      displayType
      traitType
      key
      value
    }
    isFollowedByMe
    isFollowing(who: null)
    followNftAddress
    metadata
    isDefault
    handle
    picture {
      ... on NftImage {
        contractAddress
        tokenId
        uri
        verified
      }
      ... on MediaSet {
        original {
          ...MediaFields
        }
      }
    }
    coverPicture {
      ... on NftImage {
        contractAddress
        tokenId
        uri
        verified
      }
      ... on MediaSet {
        original {
          ...MediaFields
        }
      }
    }
    ownedBy
    dispatcher {
      <span class="hljs-keyword">address</span>
    }
    stats {
      totalFollowers
      totalFollowing
      totalPosts
      totalComments
      totalMirrors
      totalPublications
      totalCollects
    }
    followModule {
      ... on FeeFollowModuleSettings {
        <span class="hljs-keyword">type</span>
        amount {
          asset {
            name
            symbol
            decimals
            <span class="hljs-keyword">address</span>
          }
          value
        }
        recipient
      }
      ... on ProfileFollowModuleSettings {
        <span class="hljs-keyword">type</span>
      }
      ... on RevertFollowModuleSettings {
        <span class="hljs-keyword">type</span>
      }
    }
  }

  fragment PublicationStatsFields on PublicationStats {
    totalAmountOfMirrors
    totalAmountOfCollects
    totalAmountOfComments
  }

  fragment MetadataOutputFields on MetadataOutput {
    name
    description
    content
    media {
      original {
        ...MediaFields
      }
    }
    attributes {
      displayType
      traitType
      value
    }
  }

  fragment Erc20Fields on Erc20 {
    name
    symbol
    decimals
    <span class="hljs-keyword">address</span>
  }

  fragment CollectModuleFields on CollectModule {
    __typename
    ... on FreeCollectModuleSettings {
      <span class="hljs-keyword">type</span>
      followerOnly
      contractAddress
    }
    ... on FeeCollectModuleSettings {
      <span class="hljs-keyword">type</span>
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
    }
    ... on LimitedFeeCollectModuleSettings {
      <span class="hljs-keyword">type</span>
      collectLimit
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
    }
    ... on LimitedTimedFeeCollectModuleSettings {
      <span class="hljs-keyword">type</span>
      collectLimit
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
      endTimestamp
    }
    ... on RevertCollectModuleSettings {
      <span class="hljs-keyword">type</span>
    }
    ... on TimedFeeCollectModuleSettings {
      <span class="hljs-keyword">type</span>
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
      endTimestamp
    }
  }

  fragment PostFields on Post {
    id
    profile {
      ...ProfileFields
    }
    stats {
      ...PublicationStatsFields
    }
    metadata {
      ...MetadataOutputFields
    }
    createdAt
    collectModule {
      ...CollectModuleFields
    }
    referenceModule {
      ... on FollowOnlyReferenceModuleSettings {
        <span class="hljs-keyword">type</span>
      }
    }
    appId
    hidden
    mirrors(by: null)
    hasCollectedByMe
  }

  fragment MirrorBaseFields on Mirror {
    id
    profile {
      ...ProfileFields
    }
    stats {
      ...PublicationStatsFields
    }
    metadata {
      ...MetadataOutputFields
    }
    createdAt
    collectModule {
      ...CollectModuleFields
    }
    referenceModule {
      ... on FollowOnlyReferenceModuleSettings {
        <span class="hljs-keyword">type</span>
      }
    }
    appId
    hidden
    hasCollectedByMe
  }

  fragment MirrorFields on Mirror {
    ...MirrorBaseFields
    mirrorOf {
      ... on Post {
        ...PostFields
      }
      ... on Comment {
        ...CommentFields
      }
    }
  }

  fragment CommentBaseFields on Comment {
    id
    profile {
      ...ProfileFields
    }
    stats {
      ...PublicationStatsFields
    }
    metadata {
      ...MetadataOutputFields
    }
    createdAt
    collectModule {
      ...CollectModuleFields
    }
    referenceModule {
      ... on FollowOnlyReferenceModuleSettings {
        <span class="hljs-keyword">type</span>
      }
    }
    appId
    hidden
    mirrors(by: null)
    hasCollectedByMe
  }

  fragment CommentFields on Comment {
    ...CommentBaseFields
    mainPost {
      ... on Post {
        ...PostFields
      }
      ... on Mirror {
        ...MirrorBaseFields
        mirrorOf {
          ... on Post {
            ...PostFields
          }
          ... on Comment {
            ...CommentMirrorOfFields
          }
        }
      }
    }
  }

  fragment CommentMirrorOfFields on Comment {
    ...CommentBaseFields
    mainPost {
      ... on Post {
        ...PostFields
      }
      ... on Mirror {
        ...MirrorBaseFields
      }
    }
  }
`;
</code></pre><p>在components/Post.js 新建一个Post.js并且写入一下代码：</p><pre data-type="codeBlock" text="// components/Post.js
export default function Post(props) {
  const post = props.post;

  return (
    &lt;div className=&quot;p-8&quot;&gt;
      &lt;div className=&quot;max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl&quot;&gt;
        &lt;div className=&quot;md:flex&quot;&gt;
          &lt;div className=&quot;p-8&quot;&gt;
            &lt;p className=&quot;mt-2 text-xs text-slate-500 whitespace-pre-line&quot;&gt;
              {post.metadata.content}
            &lt;/p&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
"><code><span class="hljs-comment">// components/Post.js</span>
export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Post</span>(<span class="hljs-params">props</span>) </span>{
  const post <span class="hljs-operator">=</span> props.post;

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"p-8"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"md:flex"</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"p-8"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>p className<span class="hljs-operator">=</span><span class="hljs-string">"mt-2 text-xs text-slate-500 whitespace-pre-line"</span><span class="hljs-operator">></span>
              {post.metadata.content}
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  );
}
</code></pre><p>将[id].js覆盖为下面的：</p><pre data-type="codeBlock" text="import { useQuery, useMutation } from &quot;@apollo/client&quot;;
import { useRouter } from &quot;next/router&quot;;
import fetchProfileQuery from &quot;../../queries/fetchProfileQuery.js&quot;;
import Profile from &quot;../../components/Profile.js&quot;;
import Post from &quot;../../components/Post.js&quot;;

export default function ProfilePage() {
  const router = useRouter();
  const { id } = router.query;

  console.log(&quot;fetching profile for&quot;, id);
  const { loading, error, data } = useQuery(fetchProfileQuery, {
    variables: {
      request: { profileId: id },
      publicationsRequest: {
        profileId: id,
        publicationTypes: [&quot;POST&quot;],
      },
    },
  });

  if (loading) return &quot;Loading..&quot;;
  if (error) return `Error! ${error.message}`;

  return (
    &lt;div className=&quot;flex flex-col p-8 items-center&quot;&gt;
      &lt;Profile profile={data.profile} displayFullProfile={true} /&gt;
      {data.publications.items.map((post, idx) =&gt; {
        return &lt;Post key={idx} post={post}/&gt;;
      })}
    &lt;/div&gt;
  );
}
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">useQuery</span>, <span class="hljs-title">useMutation</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@apollo/client"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useRouter</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"next/router"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">fetchProfileQuery</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../../queries/fetchProfileQuery.js"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Profile</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../../components/Profile.js"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Post</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../../components/Post.js"</span>;

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ProfilePage</span>(<span class="hljs-params"></span>) </span>{
  const router <span class="hljs-operator">=</span> useRouter();
  const { id } <span class="hljs-operator">=</span> router.query;

  console.log(<span class="hljs-string">"fetching profile for"</span>, id);
  const { loading, <span class="hljs-function"><span class="hljs-keyword">error</span>, <span class="hljs-title">data</span> } = <span class="hljs-title">useQuery</span>(<span class="hljs-params">fetchProfileQuery, {
    variables: {
      request: { profileId: id },
      publicationsRequest: {
        profileId: id,
        publicationTypes: [<span class="hljs-string">"POST"</span>],
      },
    },
  }</span>)</span>;

  <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="hljs-string">"Loading.."</span>;
  <span class="hljs-keyword">if</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) <span class="hljs-title"><span class="hljs-keyword">return</span></span> `<span class="hljs-title"><span class="hljs-built_in">Error</span></span>! <span class="hljs-title">$</span></span>{<span class="hljs-keyword">error</span>.message}`;

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex flex-col p-8 items-center"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>Profile profile<span class="hljs-operator">=</span>{data.profile} displayFullProfile<span class="hljs-operator">=</span>{<span class="hljs-literal">true</span>} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      {data.publications.items.map((post, idx) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
        <span class="hljs-keyword">return</span> <span class="hljs-operator">&#x3C;</span>Post key<span class="hljs-operator">=</span>{idx} post<span class="hljs-operator">=</span>{post}<span class="hljs-operator">/</span><span class="hljs-operator">></span>;
      })}
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  );
}
</code></pre><p>验证结果 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://localhost:3000/profile/0x28a2">http://localhost:3000/profile/0x28a2</a>，恭喜，你正在成为一个去中心化的社交媒体开发者。</p>]]></content:encoded>
            <author>monkey-11@newsletter.paragraph.com (Monkey)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3第九周文本教程- 使用 0x API 构建代币交换 Dapp]]></title>
            <link>https://paragraph.com/@monkey-11/alchemy-the-road-to-web3-0x-api-dapp</link>
            <guid>ixTITxBKaJHznrhXDtCD</guid>
            <pubDate>Sun, 25 Sep 2022 15:20:44 GMT</pubDate>
            <description><![CDATA[在本教程中，我们将学习如何使用0x API 交换端点它允许用户在流动性供应和使用中获取可用报价智能订单路由在分散的交易网络中拆分交易，以尽可能降低滑点，同时最大限度地降低交易成本。请注意，我们不需要编写任何智能合约来查找和结算交易！相反，0x API 允许 web3 开发人员轻松利用 0x 协议智能合约，该合约负责处理用于结算交易的所有逻辑，让 web 开发人员专注于构建最佳交易体验。 在本教程结束时，将学习如何执行以下操作：了解为什么流动性聚合很重要查询并显示ERC20 代币列表使用0x API /swap 端点设置代币限额构建一个使用web3.js连接到MetaMask的简单代币交换DApp结果展示正课开始1.clone 代码因为我们需要在浏览器中使用节点模块，所以需要安装下面的依赖# 可能权限不足 则使用sudo npm install -g browserify npm i qs # 每一步修改了代码为了最新的代码都必须执行下面的 browserify index.js --standalone bundle -o bundle.js npm install bignum...]]></description>
            <content:encoded><![CDATA[<p>在本教程中，我们将学习如何使用<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.0x.org/0x-api-swap/introduction">0x API 交换端点</a><strong>它允许用户在流动性供应和使用中</strong>获取可用报价<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.0xproject.com/0x-apis-smart-order-routing-7af0195515e5">智能订单路由</a>在分散的交易网络中拆分交易，以尽可能降低<strong>滑点</strong>，同时最大限度地降低交易成本。请注意，我们不需要编写任何智能合约来查找和结算交易！相反，0x API 允许 web3 开发人员轻松利用 0x 协议智能合约，该合约负责处理用于结算交易的所有逻辑，让 web 开发人员专注于构建最佳交易体验。</p><p>在本教程结束时，将学习如何执行以下操作：</p><ul><li><p>了解为什么<strong>流动性聚合</strong>很重要</p></li><li><p>查询并显示<strong>ERC20 代币列表</strong></p></li><li><p>使用<strong>0x API /swap 端点</strong></p></li><li><p>设置<strong>代币限额</strong></p></li><li><p>构建一个使用web3.js连接到<strong>MetaMask的简单代币交换DApp</strong></p></li></ul><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">结果展示</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/819e61b756df78d3e60356e4ce58a1729c0746bf2c11014fdb8efb556e11c2f7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">正课开始</h2><h3 id="h-1clone" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.clone 代码</h3><p>因为我们需要在浏览器中使用节点模块，所以需要安装下面的依赖</p><pre data-type="codeBlock" text="# 可能权限不足 则使用sudo
npm install -g browserify
npm i qs
# 每一步修改了代码为了最新的代码都必须执行下面的
browserify index.js --standalone bundle -o bundle.js
npm install bignumber.js
"><code># 可能权限不足 则使用sudo
npm install <span class="hljs-operator">-</span>g browserify
npm i qs
# 每一步修改了代码为了最新的代码都必须执行下面的
browserify index.js <span class="hljs-operator">-</span><span class="hljs-operator">-</span>standalone bundle <span class="hljs-operator">-</span>o bundle.js
npm install bignumber.js
</code></pre><h2 id="h-2vscode" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.使用vscode打开我们的代码</h2><p>我们将clone的代码使用VScode打开后，为了方便使用则需要安装一些扩展工具，按照下面安装即可：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b3f616c69836fc330bdfa952cf88890daf38c7e808f6670b0892f7db222286e0.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/29e45cd6c5fe43f2c0d03bb557b8413b4a7edb2e158642081b2038435654683b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>当上面都安装好后，我们使用安装的工具运行代码，会出现下面的页面，系统会自动去加载token信息。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/efa1fffe5137ea5a3576328ef5b84b649d1c953acdcc215fc591f243d677430c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/819e61b756df78d3e60356e4ce58a1729c0746bf2c11014fdb8efb556e11c2f7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>系统会自动给我们计算Gas，我们连接上钱包，点击Swap，小狐狸钱包会提示我们授权</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b6703bf7bd63879acd25d69b7d0cca3dfbd4795f4873267a835dc247cf6ec3a3.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>当我们批准授权后，小狐狸钱包会再次提醒我们去和合约交互做Swap，我们再次同意后就可以等待事物的完成。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8bf2a49a6ac0002659f637bb7d5848982e0ffdd1c1744c0092bc60b1842c373a.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>当你完成后，可以多种方式查询结构，如去区块浏览器查询，或者在自己钱包查看</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3c3e8f46ae2bd603c582db759e669e1f93d9bc0f87d95e7e0bdb04af6cfba9b4.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>注意事项：因为合理0x是使用主网的哦，切记。</strong></p><p>最后我们别忘了去填写表格哦。</p>]]></content:encoded>
            <author>monkey-11@newsletter.paragraph.com (Monkey)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3第八周文本教程- 如何在 Optimism 上构建博彩游戏]]></title>
            <link>https://paragraph.com/@monkey-11/alchemy-the-road-to-web3-optimism</link>
            <guid>3q54pkyiecvzpgM1tZd0</guid>
            <pubDate>Sun, 25 Sep 2022 15:19:49 GMT</pubDate>
            <description><![CDATA[在本周课程中，我们将介绍如何克服为区块链生成随机数的限制。我们将介绍如何为使用随机数的赌场博彩游戏构建和测试 Solidity 合约。我们还将讨论在区块链博彩游戏中防止滥用的一些策略。1.新建端点首先我们的去Alchemy网站新建Optimism的端点。2.clone代码当我们将代码clone到本地之后，因为一般代码上传不会上传依赖，所以我们去安装依赖。# 安装依赖 yarn 3.修改配置部署合约当我们将依赖安装完毕后，我们去修改我们的hardhat.config.js，修改内容如下：require("@nomiclabs/hardhat-waffle"); require('dotenv').config() // This is a sample Hardhat task. To learn how to create your own go to // https://hardhat.org/guides/create-task.html task("accounts", "Prints the list of accounts", async (taskArgs, hre...]]></description>
            <content:encoded><![CDATA[<p>在本周课程中，我们将介绍如何克服为区块链生成随机数的限制。我们将介绍如何为使用随机数的赌场博彩游戏构建和测试 Solidity 合约。我们还将讨论在区块链博彩游戏中防止滥用的一些策略。</p><h2 id="h-1" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1.新建端点</h2><p>首先我们的去<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/">Alchemy</a>网站新建Optimism的端点。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f5fa96aeb4bd80973cfe2a190671ba9de201d2e53025d992c85ab7fe5a9b492e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-2clone" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.clone代码</h2><p>当我们将代码clone到本地之后，因为一般代码上传不会上传依赖，所以我们去安装依赖。</p><pre data-type="codeBlock" text="# 安装依赖
yarn
"><code><span class="hljs-comment"># 安装依赖</span>
yarn
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6f0d03a6f27526163e66a1267a3a9b17ae42c3456df21b09183fdf5e7166aee7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3.修改配置部署合约</h2><p>当我们将依赖安装完毕后，我们去修改我们的hardhat.config.js，修改内容如下：</p><pre data-type="codeBlock" text="require(&quot;@nomiclabs/hardhat-waffle&quot;);
require(&apos;dotenv&apos;).config()

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task(&quot;accounts&quot;, &quot;Prints the list of accounts&quot;, async (taskArgs, hre) =&gt; {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more


// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * 切记私钥不要上传到仓库 切记 切记
 */

module.exports = {
  solidity: &quot;0.8.4&quot;,
  networks: {
    
    &quot;optimism&quot;: {
       url: &quot;第一步新建的Optimism的url&quot;,
       accounts: [ &quot;你的私钥&quot; ]
    }
  }
};
"><code><span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomiclabs/hardhat-waffle"</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config()

<span class="hljs-comment">// This is a sample Hardhat task. To learn how to create your own go to</span>
<span class="hljs-comment">// https://hardhat.org/guides/create-task.html</span>
task(<span class="hljs-string">"accounts"</span>, <span class="hljs-string">"Prints the list of accounts"</span>, async (taskArgs, hre) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  const accounts <span class="hljs-operator">=</span> await hre.ethers.getSigners();

  <span class="hljs-keyword">for</span> (const account of accounts) {
    console.log(account.<span class="hljs-built_in">address</span>);
  }
});

<span class="hljs-comment">// You need to export an object to set up your config</span>
<span class="hljs-comment">// Go to https://hardhat.org/config/ to learn more</span>


<span class="hljs-comment">// You need to export an object to set up your config</span>
<span class="hljs-comment">// Go to https://hardhat.org/config/ to learn more</span>

<span class="hljs-comment">/**
 * 切记私钥不要上传到仓库 切记 切记
 */</span>

module.exports <span class="hljs-operator">=</span> {
  solidity: <span class="hljs-string">"0.8.4"</span>,
  networks: {
    
    <span class="hljs-string">"optimism"</span>: {
       url: <span class="hljs-string">"第一步新建的Optimism的url"</span>,
       accounts: [ <span class="hljs-string">"你的私钥"</span> ]
    }
  }
};
</code></pre><p>修改完毕后我们在控制台输入：</p><pre data-type="codeBlock" text="yarn  hardhat console --network optimism
"><code>yarn  hardhat console <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network optimism
</code></pre><p>系统会自动给我们编译合约，因为我们加了console命令，所以会进入控制台，同时我们会生成几个文件：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/597e603c642a690f91764cd9f7050aee69a7bdf6f4fb67e69f03386154c3e76b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>上面我们已经进入了控制台，下面的命令就在控制台输入了：</p><pre data-type="codeBlock" text="# 查看自己的当前账户是否是你的小狐狸账户
signer = await ethers.getSigner();
"><code><span class="hljs-comment"># 查看自己的当前账户是否是你的小狐狸账户</span>
<span class="hljs-attr">signer</span> = await ethers.getSigner()<span class="hljs-comment">;</span>
</code></pre><pre data-type="codeBlock" text="# 查询当前账户的余额
balance0 = await ethers.provider.getBalance((await ethers.getSigner()).address)
BigNumber { value: &quot;48335146483888624&quot; }
"><code># 查询当前账户的余额
balance0 <span class="hljs-operator">=</span> await ethers.provider.getBalance((await ethers.getSigner()).<span class="hljs-built_in">address</span>)
BigNumber { <span class="hljs-built_in">value</span>: <span class="hljs-string">"48335146483888624"</span> }
</code></pre><pre data-type="codeBlock" text="#开始编译合约 
factory = ethers.getContractFactory(&quot;Casino&quot;)
# 下面这条命令会返回我们bytecode等信息
factory = await factory
# 部署合约 你的optimism一定要有费用，具体的可以看下面怎么给optimism充值
casino = await factory.deploy()
"><code><span class="hljs-comment">#开始编译合约 </span>
<span class="hljs-attr">factory</span> = ethers.getContractFactory(<span class="hljs-string">"Casino"</span>)
<span class="hljs-comment"># 下面这条命令会返回我们bytecode等信息</span>
<span class="hljs-attr">factory</span> = await factory
<span class="hljs-comment"># 部署合约 你的optimism一定要有费用，具体的可以看下面怎么给optimism充值</span>
<span class="hljs-attr">casino</span> = await factory.deploy()
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5d9ec412ad480b165c2ca6b2ae9b7e15e1832e3a2534c33b8ce62c227e341231.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>找到你部署的hash去<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://optimistic.etherscan.io/">区块浏览器</a>进行查询，当然也可以用你的钱包查询</p><h2 id="h-3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3.开始游戏</h2><p>我们仍然在控制台输入：</p><pre data-type="codeBlock" text="# 直接粘贴复制进去
const valA = ethers.utils.keccak256(0xBAD060A7)
const hashA = ethers.utils.keccak256(valA)
const valBwin = ethers.utils.keccak256(0x600D60A7)
tx1 = await casino.proposeBet(hashA,{ value: 1e5})
"><code># 直接粘贴复制进去
const valA <span class="hljs-operator">=</span> ethers.utils.keccak256(<span class="hljs-number">0xBAD060A7</span>)
const hashA <span class="hljs-operator">=</span> ethers.utils.keccak256(valA)
const valBwin <span class="hljs-operator">=</span> ethers.utils.keccak256(<span class="hljs-number">0x600D60A7</span>)
tx1 <span class="hljs-operator">=</span> await casino.proposeBet(hashA,{ <span class="hljs-built_in">value</span>: <span class="hljs-number">1e5</span>})
</code></pre><p>最后再来一次游戏就结束了：</p><pre data-type="codeBlock" text="# 如果你的value和上面的不一致，会需要重新覆盖且报错
tx2 = await casino.acceptBet(hashA, valBwin, {value: 1e5})
"><code><span class="hljs-comment"># 如果你的value和上面的不一致，会需要重新覆盖且报错</span>
<span class="hljs-attr">tx2</span> = await casi<span class="hljs-literal">no</span>.acceptBet(hashA, valBwin, {value: <span class="hljs-number">1</span>e5})
</code></pre><h3 id="h-ethoptimism" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">当你运行命令出现了下面的情况，就说明你的钱包余额不足，所以需要我们去转下ETH到optimism</h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f78f775d3deadce068233a4ad77bda7e800a3762dc3f4ebf2dbef4d363190a2b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>我们去到 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://app.optimism.io/bridge">optimism</a>的网站转入ETH即可。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b88c7cc21eb84a1143b536a3440f58f7f91528347d9cf8c79e88b322aed36da7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>monkey-11@newsletter.paragraph.com (Monkey)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3第七周文本教程- 从零开始构建 NFT 市场]]></title>
            <link>https://paragraph.com/@monkey-11/alchemy-the-road-to-web3-nft</link>
            <guid>ohVlBv0fXVI9vkC0oj7U</guid>
            <pubDate>Sun, 25 Sep 2022 15:18:38 GMT</pubDate>
            <description><![CDATA[准备工作注册一个 Alchemy 帐户并创建一个新应用程序MetaMask切换到Goerli且你的钱包里面至少有 0.1 Goerli ETH如果您没有 Goerli 地址，将 MetaMask 连接到 Goerli 网络， 接着使用 Goerli 水龙头请求 Goerli ETH. 您将需要 Goerli ETH 来部署智能合约并将 NFT 上传到您的 NFT 市场。在MetaMask中添加下面的链信息：Network Name: Goerli Test Network RPC base URL: https://eth-goerli.alchemyapi.io/v2/{INSERT YOUR API KEY} Chain ID: 5 Block Explorer URL: https://goerli.etherscan.io/1.设置存储库、设置环境变量和 Hardhat 配置本次课程我们继续使用repli来做 首先我们将本次需要的前端代码clone下来，具体操作如下：导入仓库文件导入仓库文件 导入仓库文件，等到项目倒入完成，当项目完成后，我们需要去修改配置文件导入文件导入...]]></description>
            <content:encoded><![CDATA[<h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">准备工作</h3><ul><li><p>注册一个 Alchemy 帐户并创建一个新应用程序</p></li><li><p>MetaMask切换到Goerli且你的钱包里面至少有 0.1 Goerli ETH</p></li><li><p>如果您没有 Goerli 地址，<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.alchemy.com/docs/how-to-add-alchemy-rpc-endpoints-to-metamask">将 MetaMask 连接到 Goerli 网络</a>， 接着<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://goerlifaucet.com/">使用 Goerli 水龙头请求 Goerli ETH</a>. 您将需要 Goerli ETH 来部署智能合约并将 NFT 上传到您的 NFT 市场。</p></li></ul><p>在MetaMask中添加下面的链信息：</p><ul><li><p><strong>Network Name:</strong> Goerli Test Network</p><p><strong>RPC base URL:</strong> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eth-goerli.alchemyapi.io/v2/%7BINSERT">https://eth-goerli.alchemyapi.io/v2/{INSERT</a> YOUR API KEY}</p><p><strong>Chain ID:</strong> 5</p><p><strong>Block Explorer URL:</strong> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://goerli.etherscan.io/">https://goerli.etherscan.io/</a></p></li></ul><h2 id="h-1-hardhat" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1.设置存储库、设置环境变量和 Hardhat 配置</h2><p>本次课程我们继续使用repli来做</p><p>首先我们将本次需要的前端代码clone下来，具体操作如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/423f9b0236b80e7c3cb91f0f4ce6f6863193240406d0cf52ade6d2bc73727052.png" alt="导入仓库文件" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">导入仓库文件</figcaption></figure><p>导入仓库文件</p><p>导入仓库文件，等到项目倒入完成，当项目完成后，我们需要去修改配置文件</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0a936ed20097f47f59f6fbf6f0ffe9018d450240bc8205933f8761063c07a55b.png" alt="导入文件" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">导入文件</figcaption></figure><p>导入文件</p><p>导入文件</p><p>然后在项目下执行</p><pre data-type="codeBlock" text="npm install
npm start
"><code>npm install
npm <span class="hljs-keyword">start</span>
</code></pre><p>首先我们将hardhat.config.js的内容修改如下：</p><pre data-type="codeBlock" text="require(&quot;@nomiclabs/hardhat-waffle&quot;);
require(&quot;@nomiclabs/hardhat-ethers&quot;);
const fs = require(&apos;fs&apos;);
// const infuraId = fs.readFileSync(&quot;.infuraid&quot;).toString().trim() || &quot;&quot;;
require(&apos;dotenv&apos;).config();

task(&quot;accounts&quot;, &quot;Prints the list of accounts&quot;, async (taskArgs, hre) =&gt; {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

module.exports = {
  defaultNetwork: &quot;hardhat&quot;,
  networks: {
    hardhat: {
      chainId: 1337
    },
    goerli: {
      url: process.env.REACT_APP_ALCHEMY_API_URL,
      accounts: [ process.env.REACT_APP_PRIVATE_KEY ]
    }
  },
  solidity: {
    version: &quot;0.8.4&quot;,
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      }
    }
  }
};
"><code><span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomiclabs/hardhat-waffle"</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomiclabs/hardhat-ethers"</span>);
const fs <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-comment">// const infuraId = fs.readFileSync(".infuraid").toString().trim() || "";</span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();

task(<span class="hljs-string">"accounts"</span>, <span class="hljs-string">"Prints the list of accounts"</span>, async (taskArgs, hre) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  const accounts <span class="hljs-operator">=</span> await hre.ethers.getSigners();

  <span class="hljs-keyword">for</span> (const account of accounts) {
    console.log(account.<span class="hljs-built_in">address</span>);
  }
});

module.exports <span class="hljs-operator">=</span> {
  defaultNetwork: <span class="hljs-string">"hardhat"</span>,
  networks: {
    hardhat: {
      chainId: <span class="hljs-number">1337</span>
    },
    goerli: {
      url: process.env.REACT_APP_ALCHEMY_API_URL,
      accounts: [ process.env.REACT_APP_PRIVATE_KEY ]
    }
  },
  solidity: {
    version: <span class="hljs-string">"0.8.4"</span>,
    settings: {
      optimizer: {
        enabled: <span class="hljs-literal">true</span>,
        runs: <span class="hljs-number">200</span>
      }
    }
  }
};
</code></pre><p>同时新建一个.env文件，文件容易如下，如果无法新建直接将该值替换即可</p><pre data-type="codeBlock" text="REACT_APP_ALCHEMY_API_URL=&quot;&lt;YOUR_API_URL&gt;&quot;
REACT_APP_PRIVATE_KEY=&quot;&lt;YOUR_PRIVATE_KEY&gt;&quot;
"><code><span class="hljs-attr">REACT_APP_ALCHEMY_API_URL</span>=<span class="hljs-string">"&#x3C;YOUR_API_URL>"</span>
<span class="hljs-attr">REACT_APP_PRIVATE_KEY</span>=<span class="hljs-string">"&#x3C;YOUR_PRIVATE_KEY>"</span>
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/dc6cac874e21c061dd91f74071fb8a13a6590ebfd641492b163ea4c7eb5a64fb.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>当我们上面做好了，我们在shell中输入一下命令，让系统帮助我们安装依赖等信息：</p><pre data-type="codeBlock" text="npm install dotenv --save
"><code>npm install dotenv <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save
</code></pre><h2 id="h-2-pinata-ipfs" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.使用 Piñata 将数据上传到 IPFS</h2><p>如果没有 Piñata 帐户，<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://pinata.cloud/signup">注册</a>一个即可。当注册登录进去后我们需要去获取api_key.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/bb06c581344736dae382c7c54ccc79625a14bbebeb58b9416f9fd4702b1dfb02.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>我们需要新建一个key，同时将Admin权限开启，给自己的key命名</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a62c9b654b32b58d212263280bee6ff0b569b8b166e45aeefee152a75ea764c7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>当我们新建成功后，页面会提示一个有关key的信息，将它复制到安全的地方</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f9e2c8a9eea9f0aa1f597f03f6aad5bd7827724c2b741d3f2d0fe430d2cbb7d6.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>同时将我们的.env文件进行修改：</p><pre data-type="codeBlock" text="REACT_APP_ALCHEMY_API_URL=&quot;&lt;YOUR_API_URL&gt;&quot;
REACT_APP_PRIVATE_KEY=&quot;&lt;YOUR_PRIVATE_KEY&gt;&quot;
REACT_APP_PINATA_KEY=&quot;&lt;YOUR_PINATA_KEY&gt;&quot;
REACT_APP_PINATA_SECRET=&quot;&lt;YOUR_PINATA_SECRET&gt;&quot;
"><code><span class="hljs-attr">REACT_APP_ALCHEMY_API_URL</span>=<span class="hljs-string">"&#x3C;YOUR_API_URL>"</span>
<span class="hljs-attr">REACT_APP_PRIVATE_KEY</span>=<span class="hljs-string">"&#x3C;YOUR_PRIVATE_KEY>"</span>
<span class="hljs-attr">REACT_APP_PINATA_KEY</span>=<span class="hljs-string">"&#x3C;YOUR_PINATA_KEY>"</span>
<span class="hljs-attr">REACT_APP_PINATA_SECRET</span>=<span class="hljs-string">"&#x3C;YOUR_PINATA_SECRET>"</span>
</code></pre><h2 id="h-3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3.编写合约</h2><p>现在去修改NFTMarketplace.sol这个文件，合约代码如下：</p><pre data-type="codeBlock" text="//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import &quot;hardhat/console.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Counters.sol&quot;;
import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol&quot;;
import &quot;@openzeppelin/contracts/token/ERC721/ERC721.sol&quot;;

contract NFTMarketplace is ERC721URIStorage {

    using Counters for Counters.Counter;
    //_tokenIds variable has the most recent minted tokenId
    Counters.Counter private _tokenIds;
    //Keeps track of the number of items sold on the marketplace
    Counters.Counter private _itemsSold;
    //owner is the contract address that created the smart contract
    address payable owner;
    //The fee charged by the marketplace to be allowed to list an NFT
    uint256 listPrice = 0.01 ether;

    //The structure to store info about a listed token
    struct ListedToken {
        uint256 tokenId;
        address payable owner;
        address payable seller;
        uint256 price;
        bool currentlyListed;
    }

    //the event emitted when a token is successfully listed
    event TokenListedSuccess (
        uint256 indexed tokenId,
        address owner,
        address seller,
        uint256 price,
        bool currentlyListed
    );

    //This mapping maps tokenId to token info and is helpful when retrieving details about a tokenId
    mapping(uint256 =&gt; ListedToken) private idToListedToken;

    constructor() ERC721(&quot;NFTMarketplace&quot;, &quot;NFTM&quot;) {
        owner = payable(msg.sender);
    }

    function updateListPrice(uint256 _listPrice) public payable {
        require(owner == msg.sender, &quot;Only owner can update listing price&quot;);
        listPrice = _listPrice;
    }

    function getListPrice() public view returns (uint256) {
        return listPrice;
    }

    function getLatestIdToListedToken() public view returns (ListedToken memory) {
        uint256 currentTokenId = _tokenIds.current();
        return idToListedToken[currentTokenId];
    }

    function getListedTokenForId(uint256 tokenId) public view returns (ListedToken memory) {
        return idToListedToken[tokenId];
    }

    function getCurrentToken() public view returns (uint256) {
        return _tokenIds.current();
    }

    //The first time a token is created, it is listed here
    function createToken(string memory tokenURI, uint256 price) public payable returns (uint) {
        //Increment the tokenId counter, which is keeping track of the number of minted NFTs
        _tokenIds.increment();
        uint256 newTokenId = _tokenIds.current();

        //Mint the NFT with tokenId newTokenId to the address who called createToken
        _safeMint(msg.sender, newTokenId);

        //Map the tokenId to the tokenURI (which is an IPFS URL with the NFT metadata)
        _setTokenURI(newTokenId, tokenURI);

        //Helper function to update Global variables and emit an event
        createListedToken(newTokenId, price);

        return newTokenId;
    }

    function createListedToken(uint256 tokenId, uint256 price) private {
        //Make sure the sender sent enough ETH to pay for listing
        require(msg.value == listPrice, &quot;Hopefully sending the correct price&quot;);
        //Just sanity check
        require(price &gt; 0, &quot;Make sure the price isn&apos;t negative&quot;);

        //Update the mapping of tokenId&apos;s to Token details, useful for retrieval functions
        idToListedToken[tokenId] = ListedToken(
            tokenId,
            payable(address(this)),
            payable(msg.sender),
            price,
            true
        );

        _transfer(msg.sender, address(this), tokenId);
        //Emit the event for successful transfer. The frontend parses this message and updates the end user
        emit TokenListedSuccess(
            tokenId,
            address(this),
            msg.sender,
            price,
            true
        );
    }
    
    //This will return all the NFTs currently listed to be sold on the marketplace
    function getAllNFTs() public view returns (ListedToken[] memory) {
        uint nftCount = _tokenIds.current();
        ListedToken[] memory tokens = new ListedTokenUnsupported embed;
        uint currentIndex = 0;

        //at the moment currentlyListed is true for all, if it becomes false in the future we will 
        //filter out currentlyListed == false over here
        for(uint i=0;i&lt;nftCount;i++)
        {
            uint currentId = i + 1;
            ListedToken storage currentItem = idToListedToken[currentId];
            tokens[currentIndex] = currentItem;
            currentIndex += 1;
        }
        //the array &apos;tokens&apos; has the list of all NFTs in the marketplace
        return tokens;
    }
    
    //Returns all the NFTs that the current user is owner or seller in
    function getMyNFTs() public view returns (ListedToken[] memory) {
        uint totalItemCount = _tokenIds.current();
        uint itemCount = 0;
        uint currentIndex = 0;
        
        //Important to get a count of all the NFTs that belong to the user before we can make an array for them
        for(uint i=0; i &lt; totalItemCount; i++)
        {
            if(idToListedToken[i+1].owner == msg.sender || idToListedToken[i+1].seller == msg.sender){
                itemCount += 1;
            }
        }

        //Once you have the count of relevant NFTs, create an array then store all the NFTs in it
        ListedToken[] memory items = new ListedTokenUnsupported embed;
        for(uint i=0; i &lt; totalItemCount; i++) {
            if(idToListedToken[i+1].owner == msg.sender || idToListedToken[i+1].seller == msg.sender) {
                uint currentId = i+1;
                ListedToken storage currentItem = idToListedToken[currentId];
                items[currentIndex] = currentItem;
                currentIndex += 1;
            }
        }
        return items;
    }

    function executeSale(uint256 tokenId) public payable {
        uint price = idToListedToken[tokenId].price;
        address seller = idToListedToken[tokenId].seller;
        require(msg.value == price, &quot;Please submit the asking price in order to complete the purchase&quot;);

        //update the details of the token
        idToListedToken[tokenId].currentlyListed = true;
        idToListedToken[tokenId].seller = payable(msg.sender);
        _itemsSold.increment();

        //Actually transfer the token to the new owner
        _transfer(address(this), msg.sender, tokenId);
        //approve the marketplace to sell NFTs on your behalf
        approve(address(this), tokenId);

        //Transfer the listing fee to the marketplace creator
        payable(owner).transfer(listPrice);
        //Transfer the proceeds from the sale to the seller of the NFT
        payable(seller).transfer(msg.value);
    }

    //We might add a resell token function in the future
    //In that case, tokens won&apos;t be listed by default but users can send a request to actually list a token
    //Currently NFTs are listed by default
}
"><code><span class="hljs-comment">//SPDX-License-Identifier: Unlicense</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"hardhat/console.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/utils/Counters.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/ERC721.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">NFTMarketplace</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC721URIStorage</span> </span>{

    <span class="hljs-keyword">using</span> <span class="hljs-title">Counters</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">Counters</span>.<span class="hljs-title">Counter</span>;
    <span class="hljs-comment">//_tokenIds variable has the most recent minted tokenId</span>
    Counters.Counter <span class="hljs-keyword">private</span> _tokenIds;
    <span class="hljs-comment">//Keeps track of the number of items sold on the marketplace</span>
    Counters.Counter <span class="hljs-keyword">private</span> _itemsSold;
    <span class="hljs-comment">//owner is the contract address that created the smart contract</span>
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">payable</span> owner;
    <span class="hljs-comment">//The fee charged by the marketplace to be allowed to list an NFT</span>
    <span class="hljs-keyword">uint256</span> listPrice <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.01</span> <span class="hljs-literal">ether</span>;

    <span class="hljs-comment">//The structure to store info about a listed token</span>
    <span class="hljs-keyword">struct</span> <span class="hljs-title">ListedToken</span> {
        <span class="hljs-keyword">uint256</span> tokenId;
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">payable</span> owner;
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">payable</span> seller;
        <span class="hljs-keyword">uint256</span> price;
        <span class="hljs-keyword">bool</span> currentlyListed;
    }

    <span class="hljs-comment">//the event emitted when a token is successfully listed</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">TokenListedSuccess</span> (<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> tokenId,
        <span class="hljs-keyword">address</span> owner,
        <span class="hljs-keyword">address</span> seller,
        <span class="hljs-keyword">uint256</span> price,
        <span class="hljs-keyword">bool</span> currentlyListed
    </span>)</span>;

    <span class="hljs-comment">//This mapping maps tokenId to token info and is helpful when retrieving details about a tokenId</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> ListedToken) <span class="hljs-keyword">private</span> idToListedToken;

    <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">"NFTMarketplace"</span>, <span class="hljs-string">"NFTM"</span></span>) </span>{
        owner <span class="hljs-operator">=</span> <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateListPrice</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _listPrice</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        <span class="hljs-built_in">require</span>(owner <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-string">"Only owner can update listing price"</span>);
        listPrice <span class="hljs-operator">=</span> _listPrice;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getListPrice</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</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-keyword">return</span> listPrice;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getLatestIdToListedToken</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</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">ListedToken <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">uint256</span> currentTokenId <span class="hljs-operator">=</span> _tokenIds.current();
        <span class="hljs-keyword">return</span> idToListedToken[currentTokenId];
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getListedTokenForId</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</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">ListedToken <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> idToListedToken[tokenId];
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCurrentToken</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</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-keyword">return</span> _tokenIds.current();
    }

    <span class="hljs-comment">//The first time a token is created, it is listed here</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createToken</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> tokenURI, <span class="hljs-keyword">uint256</span> price</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint</span></span>) </span>{
        <span class="hljs-comment">//Increment the tokenId counter, which is keeping track of the number of minted NFTs</span>
        _tokenIds.increment();
        <span class="hljs-keyword">uint256</span> newTokenId <span class="hljs-operator">=</span> _tokenIds.current();

        <span class="hljs-comment">//Mint the NFT with tokenId newTokenId to the address who called createToken</span>
        _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, newTokenId);

        <span class="hljs-comment">//Map the tokenId to the tokenURI (which is an IPFS URL with the NFT metadata)</span>
        _setTokenURI(newTokenId, tokenURI);

        <span class="hljs-comment">//Helper function to update Global variables and emit an event</span>
        createListedToken(newTokenId, price);

        <span class="hljs-keyword">return</span> newTokenId;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createListedToken</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-keyword">uint256</span> price</span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> </span>{
        <span class="hljs-comment">//Make sure the sender sent enough ETH to pay for listing</span>
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> listPrice, <span class="hljs-string">"Hopefully sending the correct price"</span>);
        <span class="hljs-comment">//Just sanity check</span>
        <span class="hljs-built_in">require</span>(price <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Make sure the price isn't negative"</span>);

        <span class="hljs-comment">//Update the mapping of tokenId's to Token details, useful for retrieval functions</span>
        idToListedToken[tokenId] <span class="hljs-operator">=</span> ListedToken(
            tokenId,
            <span class="hljs-keyword">payable</span>(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)),
            <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>),
            price,
            <span class="hljs-literal">true</span>
        );

        _transfer(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), tokenId);
        <span class="hljs-comment">//Emit the event for successful transfer. The frontend parses this message and updates the end user</span>
        <span class="hljs-keyword">emit</span> TokenListedSuccess(
            tokenId,
            <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>),
            <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            price,
            <span class="hljs-literal">true</span>
        );
    }
    
    <span class="hljs-comment">//This will return all the NFTs currently listed to be sold on the marketplace</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAllNFTs</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</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">ListedToken[] <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">uint</span> nftCount <span class="hljs-operator">=</span> _tokenIds.current();
        ListedToken[] <span class="hljs-keyword">memory</span> tokens <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ListedTokenUnsupported embed;
        <span class="hljs-keyword">uint</span> currentIndex <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;

        <span class="hljs-comment">//at the moment currentlyListed is true for all, if it becomes false in the future we will </span>
        <span class="hljs-comment">//filter out currentlyListed == false over here</span>
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">uint</span> i<span class="hljs-operator">=</span><span class="hljs-number">0</span>;i<span class="hljs-operator">&#x3C;</span>nftCount;i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>)
        {
            <span class="hljs-keyword">uint</span> currentId <span class="hljs-operator">=</span> i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
            ListedToken <span class="hljs-keyword">storage</span> currentItem <span class="hljs-operator">=</span> idToListedToken[currentId];
            tokens[currentIndex] <span class="hljs-operator">=</span> currentItem;
            currentIndex <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
        }
        <span class="hljs-comment">//the array 'tokens' has the list of all NFTs in the marketplace</span>
        <span class="hljs-keyword">return</span> tokens;
    }
    
    <span class="hljs-comment">//Returns all the NFTs that the current user is owner or seller in</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMyNFTs</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</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">ListedToken[] <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">uint</span> totalItemCount <span class="hljs-operator">=</span> _tokenIds.current();
        <span class="hljs-keyword">uint</span> itemCount <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
        <span class="hljs-keyword">uint</span> currentIndex <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
        
        <span class="hljs-comment">//Important to get a count of all the NFTs that belong to the user before we can make an array for them</span>
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">uint</span> i<span class="hljs-operator">=</span><span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> totalItemCount; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>)
        {
            <span class="hljs-keyword">if</span>(idToListedToken[i<span class="hljs-operator">+</span><span class="hljs-number">1</span>].owner <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">|</span><span class="hljs-operator">|</span> idToListedToken[i<span class="hljs-operator">+</span><span class="hljs-number">1</span>].seller <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>){
                itemCount <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
            }
        }

        <span class="hljs-comment">//Once you have the count of relevant NFTs, create an array then store all the NFTs in it</span>
        ListedToken[] <span class="hljs-keyword">memory</span> items <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ListedTokenUnsupported embed;
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">uint</span> i<span class="hljs-operator">=</span><span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> totalItemCount; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">if</span>(idToListedToken[i<span class="hljs-operator">+</span><span class="hljs-number">1</span>].owner <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">|</span><span class="hljs-operator">|</span> idToListedToken[i<span class="hljs-operator">+</span><span class="hljs-number">1</span>].seller <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) {
                <span class="hljs-keyword">uint</span> currentId <span class="hljs-operator">=</span> i<span class="hljs-operator">+</span><span class="hljs-number">1</span>;
                ListedToken <span class="hljs-keyword">storage</span> currentItem <span class="hljs-operator">=</span> idToListedToken[currentId];
                items[currentIndex] <span class="hljs-operator">=</span> currentItem;
                currentIndex <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
            }
        }
        <span class="hljs-keyword">return</span> items;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeSale</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        <span class="hljs-keyword">uint</span> price <span class="hljs-operator">=</span> idToListedToken[tokenId].price;
        <span class="hljs-keyword">address</span> seller <span class="hljs-operator">=</span> idToListedToken[tokenId].seller;
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> price, <span class="hljs-string">"Please submit the asking price in order to complete the purchase"</span>);

        <span class="hljs-comment">//update the details of the token</span>
        idToListedToken[tokenId].currentlyListed <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
        idToListedToken[tokenId].seller <span class="hljs-operator">=</span> <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
        _itemsSold.increment();

        <span class="hljs-comment">//Actually transfer the token to the new owner</span>
        _transfer(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, tokenId);
        <span class="hljs-comment">//approve the marketplace to sell NFTs on your behalf</span>
        approve(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), tokenId);

        <span class="hljs-comment">//Transfer the listing fee to the marketplace creator</span>
        <span class="hljs-keyword">payable</span>(owner).<span class="hljs-built_in">transfer</span>(listPrice);
        <span class="hljs-comment">//Transfer the proceeds from the sale to the seller of the NFT</span>
        <span class="hljs-keyword">payable</span>(seller).<span class="hljs-built_in">transfer</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>);
    }

    <span class="hljs-comment">//We might add a resell token function in the future</span>
    <span class="hljs-comment">//In that case, tokens won't be listed by default but users can send a request to actually list a token</span>
    <span class="hljs-comment">//Currently NFTs are listed by default</span>
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5b9c0529d85cc501b5737fd8e7502eb17b95bd43f8eaea3730a6cc665026c65e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-4-goerli" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">4.在 Goerli 上部署智能合约</h2><p>找到部署合约的deploy.js，将下面的代码写入：</p><pre data-type="codeBlock" text="const { ethers } = require(&quot;hardhat&quot;);
const hre = require(&quot;hardhat&quot;);
const fs = require(&quot;fs&quot;);

async function main() {
  //get the signer that we will use to deploy
  const [deployer] = await ethers.getSigners();
  
  //Get the NFTMarketplace smart contract object and deploy it
  const Marketplace = await hre.ethers.getContractFactory(&quot;NFTMarketplace&quot;);
  const marketplace = await Marketplace.deploy();

  await marketplace.deployed();
  
  //Pull the address and ABI out while you deploy, since that will be key in interacting with the smart contract later
  const data = {
    address: marketplace.address,
    abi: JSON.parse(marketplace.interface.format(&apos;json&apos;))
  }

  //This writes the ABI and address to the marketplace.json
  //This data is then used by frontend files to connect with the smart contract
  fs.writeFileSync(&apos;./src/Marketplace.json&apos;, JSON.stringify(data))
}

main()
  .then(() =&gt; process.exit(0))
  .catch((error) =&gt; {
    console.error(error);
    process.exit(1);
  });
"><code>const { ethers } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);
const hre <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);
const fs <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">//get the signer that we will use to deploy</span>
  const [deployer] <span class="hljs-operator">=</span> await ethers.getSigners();
  
  <span class="hljs-comment">//Get the NFTMarketplace smart contract object and deploy it</span>
  const Marketplace <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(<span class="hljs-string">"NFTMarketplace"</span>);
  const marketplace <span class="hljs-operator">=</span> await Marketplace.deploy();

  await marketplace.deployed();
  
  <span class="hljs-comment">//Pull the address and ABI out while you deploy, since that will be key in interacting with the smart contract later</span>
  const data <span class="hljs-operator">=</span> {
    <span class="hljs-keyword">address</span>: marketplace.<span class="hljs-built_in">address</span>,
    <span class="hljs-built_in">abi</span>: JSON.parse(marketplace.interface.format(<span class="hljs-string">'json'</span>))
  }

  <span class="hljs-comment">//This writes the ABI and address to the marketplace.json</span>
  <span class="hljs-comment">//This data is then used by frontend files to connect with the smart contract</span>
  fs.writeFileSync(<span class="hljs-string">'./src/Marketplace.json'</span>, JSON.stringify(data))
}

main()
  .then(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> process.exit(<span class="hljs-number">0</span>))
  .catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
    console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    process.exit(<span class="hljs-number">1</span>);
  });
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c06070c784c0a44a0fd672d7feb9c214edc20c40daaabc7bc73fd6a963a9176b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2dbd348f157e903e7ac0908ff8ea15050cf411d5e2b429cee9262963353136f0.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>使用下面的命令部署合约</p><pre data-type="codeBlock" text="npx hardhat run --network goerli scripts/deploy.js
"><code>npx hardhat run <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network goerli scripts<span class="hljs-operator">/</span>deploy.js
</code></pre><p>当你看到生成了一下的文件则说明你已经成功部署了</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fbc94063be713bf3bdf2dbf38d964a5adbe8b4540492fe8603decc8d5a32ae41.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>我们在部署的json文件中也可以找到合约的地址，我们去浏览器查看下合约地址是：</p><p>0x75E88d1B05014A5B8B24EfA73e3A740ae050063a</p><h2 id="h-5-nft-yuan-pinata" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">5.添加将 NFT 元数据上传到 Piñata 的功能</h2><p>我们首先新建一个pinata.js并将下面的代码填写进去，记住如果是硬编码，需要替换你的key等信息：</p><pre data-type="codeBlock" text="//require(&apos;dotenv&apos;).config();
const key = process.env.REACT_APP_PINATA_KEY;
const secret = process.env.REACT_APP_PINATA_SECRET;

const axios = require(&apos;axios&apos;);
const FormData = require(&apos;form-data&apos;);

export const uploadJSONToIPFS = async(JSONBody) =&gt; {
    const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`;
    //making axios POST request to Pinata ⬇️
    return axios 
        .post(url, JSONBody, {
            headers: {
                pinata_api_key: key,
                pinata_secret_api_key: secret,
            }
        })
        .then(function (response) {
           return {
               success: true,
               pinataURL: &quot;https://gateway.pinata.cloud/ipfs/&quot; + response.data.IpfsHash
           };
        })
        .catch(function (error) {
            console.log(error)
            return {
                success: false,
                message: error.message,
            }

    });
};

export const uploadFileToIPFS = async(file) =&gt; {
    const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
    //making axios POST request to Pinata ⬇️
    
    let data = new FormData();
    data.append(&apos;file&apos;, file);

    const metadata = JSON.stringify({
        name: &apos;testname&apos;,
        keyvalues: {
            exampleKey: &apos;exampleValue&apos;
        }
    });
    data.append(&apos;pinataMetadata&apos;, metadata);

    //pinataOptions are optional
    const pinataOptions = JSON.stringify({
        cidVersion: 0,
        customPinPolicy: {
            regions: [
                {
                    id: &apos;FRA1&apos;,
                    desiredReplicationCount: 1
                },
                {
                    id: &apos;NYC1&apos;,
                    desiredReplicationCount: 2
                }
            ]
        }
    });
    data.append(&apos;pinataOptions&apos;, pinataOptions);

    return axios 
        .post(url, data, {
            maxBodyLength: &apos;Infinity&apos;,
            headers: {
                &apos;Content-Type&apos;: `multipart/form-data; boundary=${data._boundary}`,
                pinata_api_key: key,
                pinata_secret_api_key: secret,
            }
        })
        .then(function (response) {
            console.log(&quot;image uploaded&quot;, response.data.IpfsHash)
            return {
               success: true,
               pinataURL: &quot;https://gateway.pinata.cloud/ipfs/&quot; + response.data.IpfsHash
           };
        })
        .catch(function (error) {
            console.log(error)
            return {
                success: false,
                message: error.message,
            }

    });
};
"><code><span class="hljs-comment">//require('dotenv').config();</span>
const key <span class="hljs-operator">=</span> process.env.REACT_APP_PINATA_KEY;
const secret <span class="hljs-operator">=</span> process.env.REACT_APP_PINATA_SECRET;

const axios <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'axios'</span>);
const FormData <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'form-data'</span>);

export const uploadJSONToIPFS <span class="hljs-operator">=</span> async(JSONBody) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const url <span class="hljs-operator">=</span> `https:<span class="hljs-comment">//api.pinata.cloud/pinning/pinJSONToIPFS`;</span>
    <span class="hljs-comment">//making axios POST request to Pinata ⬇️</span>
    <span class="hljs-keyword">return</span> axios 
        .post(url, JSONBody, {
            headers: {
                pinata_api_key: key,
                pinata_secret_api_key: secret,
            }
        })
        .then(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">response</span>) </span>{
           <span class="hljs-keyword">return</span> {
               success: <span class="hljs-literal">true</span>,
               pinataURL: <span class="hljs-string">"https://gateway.pinata.cloud/ipfs/"</span> <span class="hljs-operator">+</span> response.data.IpfsHash
           };
        })
        .catch(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-keyword">error</span></span>) </span>{
            console.log(<span class="hljs-function"><span class="hljs-keyword">error</span>)
            <span class="hljs-title"><span class="hljs-keyword">return</span></span> </span>{
                success: <span class="hljs-literal">false</span>,
                message: <span class="hljs-keyword">error</span>.message,
            }

    });
};

export const uploadFileToIPFS <span class="hljs-operator">=</span> async(file) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const url <span class="hljs-operator">=</span> `https:<span class="hljs-comment">//api.pinata.cloud/pinning/pinFileToIPFS`;</span>
    <span class="hljs-comment">//making axios POST request to Pinata ⬇️</span>
    
    let data <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> FormData();
    data.append(<span class="hljs-string">'file'</span>, file);

    const metadata <span class="hljs-operator">=</span> JSON.stringify({
        name: <span class="hljs-string">'testname'</span>,
        keyvalues: {
            exampleKey: <span class="hljs-string">'exampleValue'</span>
        }
    });
    data.append(<span class="hljs-string">'pinataMetadata'</span>, metadata);

    <span class="hljs-comment">//pinataOptions are optional</span>
    const pinataOptions <span class="hljs-operator">=</span> JSON.stringify({
        cidVersion: <span class="hljs-number">0</span>,
        customPinPolicy: {
            regions: [
                {
                    id: <span class="hljs-string">'FRA1'</span>,
                    desiredReplicationCount: <span class="hljs-number">1</span>
                },
                {
                    id: <span class="hljs-string">'NYC1'</span>,
                    desiredReplicationCount: <span class="hljs-number">2</span>
                }
            ]
        }
    });
    data.append(<span class="hljs-string">'pinataOptions'</span>, pinataOptions);

    <span class="hljs-keyword">return</span> axios 
        .post(url, data, {
            maxBodyLength: <span class="hljs-string">'Infinity'</span>,
            headers: {
                <span class="hljs-string">'Content-Type'</span>: `multipart<span class="hljs-operator">/</span>form<span class="hljs-operator">-</span>data; boundary<span class="hljs-operator">=</span>${data._boundary}`,
                pinata_api_key: key,
                pinata_secret_api_key: secret,
            }
        })
        .then(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">response</span>) </span>{
            console.log(<span class="hljs-string">"image uploaded"</span>, response.data.IpfsHash)
            <span class="hljs-keyword">return</span> {
               success: <span class="hljs-literal">true</span>,
               pinataURL: <span class="hljs-string">"https://gateway.pinata.cloud/ipfs/"</span> <span class="hljs-operator">+</span> response.data.IpfsHash
           };
        })
        .catch(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-keyword">error</span></span>) </span>{
            console.log(<span class="hljs-function"><span class="hljs-keyword">error</span>)
            <span class="hljs-title"><span class="hljs-keyword">return</span></span> </span>{
                success: <span class="hljs-literal">false</span>,
                message: <span class="hljs-keyword">error</span>.message,
            }

    });
};
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/804136a193e9608445cb8fdb8f6bbcb4a00b0b7f114676cc0264efe2c3a88f86.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-6" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">6.将前端与智能合约集成</h2><p>为了使平台无缝工作，将前端与智能合约中的功能集成，需要将下面的代码直接覆盖即可</p><p>src/components/SellNFT.js：</p><pre data-type="codeBlock" text="import Navbar from &quot;./Navbar&quot;;
import { useState } from &quot;react&quot;;
import { uploadFileToIPFS, uploadJSONToIPFS } from &quot;../pinata&quot;;
import Marketplace from &apos;../Marketplace.json&apos;;
import { useLocation } from &quot;react-router&quot;;

export default function SellNFT() {
  const [formParams, updateFormParams] = useState({ name: &apos;&apos;, description: &apos;&apos;, price: &apos;&apos; });
  const [fileURL, setFileURL] = useState(null);
  const ethers = require(&quot;ethers&quot;);
  const [message, updateMessage] = useState(&apos;&apos;);
  const location = useLocation();
  //This function uploads the NFT image to IPFS
  async function OnChangeFile(e) {
    var file = e.target.files[0];
    //check for file extension
    try {
      //upload the file to IPFS
      const response = await uploadFileToIPFS(file);
      if (response.success === true) {
        console.log(&quot;Uploaded image to Pinata: &quot;, response.pinataURL)
        setFileURL(response.pinataURL);
      }
    }
    catch (e) {
      console.log(&quot;Error during file upload&quot;, e);
    }
  }

  //This function uploads the metadata to IPDS
  async function uploadMetadataToIPFS() {
    const { name, description, price } = formParams;
    //Make sure that none of the fields are empty
    if (!name || !description || !price || !fileURL)
      return;

    const nftJSON = {
      name, description, price, image: fileURL
    }

    try {
      //upload the metadata JSON to IPFS
      const response = await uploadJSONToIPFS(nftJSON);
      if (response.success === true) {
        console.log(&quot;Uploaded JSON to Pinata: &quot;, response)
        return response.pinataURL;
      }
    }
    catch (e) {
      console.log(&quot;error uploading JSON metadata:&quot;, e)
    }
  }

  async function listNFT(e) {
    e.preventDefault();

    //Upload data to IPFS
    try {
      const metadataURL = await uploadMetadataToIPFS();
      //After adding your Hardhat network to your metamask, this code will get providers and signers
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      updateMessage(&quot;Please wait.. uploading (upto 5 mins)&quot;)

      //Pull the deployed contract instance
      let contract = new ethers.Contract(Marketplace.address, Marketplace.abi, signer)

      //massage the params to be sent to the create NFT request
      const price = ethers.utils.parseUnits(formParams.price, &apos;ether&apos;)
      let listingPrice = await contract.getListPrice()
      listingPrice = listingPrice.toString()

      //actually create the NFT
      let transaction = await contract.createToken(metadataURL, price, { value: listingPrice })
      await transaction.wait()

      alert(&quot;Successfully listed your NFT!&quot;);
      updateMessage(&quot;&quot;);
      updateFormParams({ name: &apos;&apos;, description: &apos;&apos;, price: &apos;&apos; });
      window.location.replace(&quot;/&quot;)
    }
    catch (e) {
      alert(&quot;Upload error&quot; + e)
    }
  }

  return (
    &lt;div className=&quot;&quot;&gt;
      &lt;Navbar&gt;&lt;/Navbar&gt;
      &lt;div className=&quot;flex flex-col place-items-center mt-10&quot; id=&quot;nftForm&quot;&gt;
        &lt;form className=&quot;bg-white shadow-md rounded px-8 pt-4 pb-8 mb-4&quot;&gt;
          &lt;h3 className=&quot;text-center font-bold text-purple-500 mb-8&quot;&gt;Upload your NFT to the marketplace&lt;/h3&gt;
          &lt;div className=&quot;mb-4&quot;&gt;
            &lt;label className=&quot;block text-purple-500 text-sm font-bold mb-2&quot; htmlFor=&quot;name&quot;&gt;NFT Name&lt;/label&gt;
            &lt;input className=&quot;shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline&quot; id=&quot;name&quot; type=&quot;text&quot; placeholder=&quot;Axie#4563&quot; onChange={e =&gt; updateFormParams({ ...formParams, name: e.target.value })} value={formParams.name}&gt;&lt;/input&gt;
          &lt;/div&gt;
          &lt;div className=&quot;mb-6&quot;&gt;
            &lt;label className=&quot;block text-purple-500 text-sm font-bold mb-2&quot; htmlFor=&quot;description&quot;&gt;NFT Description&lt;/label&gt;
            &lt;textarea className=&quot;shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline&quot; cols=&quot;40&quot; rows=&quot;5&quot; id=&quot;description&quot; type=&quot;text&quot; placeholder=&quot;Axie Infinity Collection&quot; value={formParams.description} onChange={e =&gt; updateFormParams({ ...formParams, description: e.target.value })}&gt;&lt;/textarea&gt;
          &lt;/div&gt;
          &lt;div className=&quot;mb-6&quot;&gt;
            &lt;label className=&quot;block text-purple-500 text-sm font-bold mb-2&quot; htmlFor=&quot;price&quot;&gt;Price (in ETH)&lt;/label&gt;
            &lt;input className=&quot;shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline&quot; type=&quot;number&quot; placeholder=&quot;Min 0.01 ETH&quot; step=&quot;0.01&quot; value={formParams.price} onChange={e =&gt; updateFormParams({ ...formParams, price: e.target.value })}&gt;&lt;/input&gt;
          &lt;/div&gt;
          &lt;div&gt;
            &lt;label className=&quot;block text-purple-500 text-sm font-bold mb-2&quot; htmlFor=&quot;image&quot;&gt;Upload Image&lt;/label&gt;
            &lt;input type={&quot;file&quot;} onChange={&quot;&quot;}&gt;&lt;/input&gt;
          &lt;/div&gt;
          &lt;br&gt;&lt;/br&gt;
          &lt;div className=&quot;text-green text-center&quot;&gt;{message}&lt;/div&gt;
          &lt;button onClick={&quot;&quot;} className=&quot;font-bold mt-10 w-full bg-purple-500 text-white rounded p-2 shadow-lg&quot;&gt;
            List NFT
          &lt;/button&gt;
        &lt;/form&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  )
}
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">Navbar</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./Navbar"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">uploadFileToIPFS</span>, <span class="hljs-title">uploadJSONToIPFS</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../pinata"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Marketplace</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'../Marketplace.json'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useLocation</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react-router"</span>;

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SellNFT</span>(<span class="hljs-params"></span>) </span>{
  const [formParams, updateFormParams] <span class="hljs-operator">=</span> useState({ name: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>, price: <span class="hljs-string">''</span> });
  const [fileURL, setFileURL] <span class="hljs-operator">=</span> useState(null);
  const ethers <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"ethers"</span>);
  const [message, updateMessage] <span class="hljs-operator">=</span> useState(<span class="hljs-string">''</span>);
  const location <span class="hljs-operator">=</span> useLocation();
  <span class="hljs-comment">//This function uploads the NFT image to IPFS</span>
  async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">OnChangeFile</span>(<span class="hljs-params">e</span>) </span>{
    <span class="hljs-keyword">var</span> file <span class="hljs-operator">=</span> e.target.files[<span class="hljs-number">0</span>];
    <span class="hljs-comment">//check for file extension</span>
    <span class="hljs-keyword">try</span> {
      <span class="hljs-comment">//upload the file to IPFS</span>
      const response <span class="hljs-operator">=</span> await uploadFileToIPFS(file);
      <span class="hljs-keyword">if</span> (response.success <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-literal">true</span>) {
        console.log(<span class="hljs-string">"Uploaded image to Pinata: "</span>, response.pinataURL)
        setFileURL(response.pinataURL);
      }
    }
    <span class="hljs-keyword">catch</span> (e) {
      console.log(<span class="hljs-string">"Error during file upload"</span>, e);
    }
  }

  <span class="hljs-comment">//This function uploads the metadata to IPDS</span>
  async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uploadMetadataToIPFS</span>(<span class="hljs-params"></span>) </span>{
    const { name, description, price } <span class="hljs-operator">=</span> formParams;
    <span class="hljs-comment">//Make sure that none of the fields are empty</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>name <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-operator">!</span>description <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-operator">!</span>price <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-operator">!</span>fileURL)
      <span class="hljs-keyword">return</span>;

    const nftJSON <span class="hljs-operator">=</span> {
      name, description, price, image: fileURL
    }

    <span class="hljs-keyword">try</span> {
      <span class="hljs-comment">//upload the metadata JSON to IPFS</span>
      const response <span class="hljs-operator">=</span> await uploadJSONToIPFS(nftJSON);
      <span class="hljs-keyword">if</span> (response.success <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-literal">true</span>) {
        console.log(<span class="hljs-string">"Uploaded JSON to Pinata: "</span>, response)
        <span class="hljs-keyword">return</span> response.pinataURL;
      }
    }
    <span class="hljs-keyword">catch</span> (e) {
      console.log(<span class="hljs-string">"error uploading JSON metadata:"</span>, e)
    }
  }

  async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">listNFT</span>(<span class="hljs-params">e</span>) </span>{
    e.preventDefault();

    <span class="hljs-comment">//Upload data to IPFS</span>
    <span class="hljs-keyword">try</span> {
      const metadataURL <span class="hljs-operator">=</span> await uploadMetadataToIPFS();
      <span class="hljs-comment">//After adding your Hardhat network to your metamask, this code will get providers and signers</span>
      const provider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(window.ethereum);
      const signer <span class="hljs-operator">=</span> provider.getSigner();
      updateMessage(<span class="hljs-string">"Please wait.. uploading (upto 5 mins)"</span>)

      <span class="hljs-comment">//Pull the deployed contract instance</span>
      let <span class="hljs-class"><span class="hljs-keyword">contract</span> = <span class="hljs-title"><span class="hljs-keyword">new</span></span> <span class="hljs-title">ethers</span>.<span class="hljs-title">Contract</span>(<span class="hljs-params">Marketplace.<span class="hljs-keyword">address</span>, Marketplace.<span class="hljs-built_in">abi</span>, signer</span>)

      <span class="hljs-comment">//massage the params to be sent to the create NFT request</span>
      <span class="hljs-title">const</span> <span class="hljs-title">price</span> = <span class="hljs-title">ethers</span>.<span class="hljs-title">utils</span>.<span class="hljs-title">parseUnits</span>(<span class="hljs-params">formParams.price, <span class="hljs-string">'ether'</span></span>)
      <span class="hljs-title">let</span> <span class="hljs-title">listingPrice</span> = <span class="hljs-title">await</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">getListPrice</span>(<span class="hljs-params"></span>)
      <span class="hljs-title">listingPrice</span> = <span class="hljs-title">listingPrice</span>.<span class="hljs-title">toString</span>(<span class="hljs-params"></span>)

      <span class="hljs-comment">//actually create the NFT</span>
      <span class="hljs-title">let</span> <span class="hljs-title">transaction</span> = <span class="hljs-title">await</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">createToken</span>(<span class="hljs-params">metadataURL, price, { value: listingPrice }</span>)
      <span class="hljs-title">await</span> <span class="hljs-title">transaction</span>.<span class="hljs-title">wait</span>(<span class="hljs-params"></span>)

      <span class="hljs-title">alert</span>(<span class="hljs-params"><span class="hljs-string">"Successfully listed your NFT!"</span></span>);
      <span class="hljs-title">updateMessage</span>(<span class="hljs-params"><span class="hljs-string">""</span></span>);
      <span class="hljs-title">updateFormParams</span>(<span class="hljs-params">{ name: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>, price: <span class="hljs-string">''</span> }</span>);
      <span class="hljs-title">window</span>.<span class="hljs-title">location</span>.<span class="hljs-title">replace</span>(<span class="hljs-params"><span class="hljs-string">"/"</span></span>)
    }
    <span class="hljs-title"><span class="hljs-keyword">catch</span></span> (<span class="hljs-params">e</span>) </span>{
      alert(<span class="hljs-string">"Upload error"</span> <span class="hljs-operator">+</span> e)
    }
  }

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">""</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>Navbar<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Navbar<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex flex-col place-items-center mt-10"</span> id<span class="hljs-operator">=</span><span class="hljs-string">"nftForm"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>form className<span class="hljs-operator">=</span><span class="hljs-string">"bg-white shadow-md rounded px-8 pt-4 pb-8 mb-4"</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>h3 className<span class="hljs-operator">=</span><span class="hljs-string">"text-center font-bold text-purple-500 mb-8"</span><span class="hljs-operator">></span>Upload your NFT to the marketplace<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h3<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"mb-4"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>label className<span class="hljs-operator">=</span><span class="hljs-string">"block text-purple-500 text-sm font-bold mb-2"</span> htmlFor<span class="hljs-operator">=</span><span class="hljs-string">"name"</span><span class="hljs-operator">></span>NFT Name<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>label<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>input className<span class="hljs-operator">=</span><span class="hljs-string">"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"</span> id<span class="hljs-operator">=</span><span class="hljs-string">"name"</span> <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"text"</span> placeholder<span class="hljs-operator">=</span><span class="hljs-string">"Axie#4563"</span> onChange<span class="hljs-operator">=</span>{e <span class="hljs-operator">=</span><span class="hljs-operator">></span> updateFormParams({ ...formParams, name: e.target.<span class="hljs-built_in">value</span> })} value<span class="hljs-operator">=</span>{formParams.<span class="hljs-built_in">name</span>}<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>input<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"mb-6"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>label className<span class="hljs-operator">=</span><span class="hljs-string">"block text-purple-500 text-sm font-bold mb-2"</span> htmlFor<span class="hljs-operator">=</span><span class="hljs-string">"description"</span><span class="hljs-operator">></span>NFT Description<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>label<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>textarea className<span class="hljs-operator">=</span><span class="hljs-string">"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"</span> cols<span class="hljs-operator">=</span><span class="hljs-string">"40"</span> rows<span class="hljs-operator">=</span><span class="hljs-string">"5"</span> id<span class="hljs-operator">=</span><span class="hljs-string">"description"</span> <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"text"</span> placeholder<span class="hljs-operator">=</span><span class="hljs-string">"Axie Infinity Collection"</span> value<span class="hljs-operator">=</span>{formParams.description} onChange<span class="hljs-operator">=</span>{e <span class="hljs-operator">=</span><span class="hljs-operator">></span> updateFormParams({ ...formParams, description: e.target.<span class="hljs-built_in">value</span> })}<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>textarea<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"mb-6"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>label className<span class="hljs-operator">=</span><span class="hljs-string">"block text-purple-500 text-sm font-bold mb-2"</span> htmlFor<span class="hljs-operator">=</span><span class="hljs-string">"price"</span><span class="hljs-operator">></span>Price (in ETH)<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>label<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>input className<span class="hljs-operator">=</span><span class="hljs-string">"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"</span> <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"number"</span> placeholder<span class="hljs-operator">=</span><span class="hljs-string">"Min 0.01 ETH"</span> step<span class="hljs-operator">=</span><span class="hljs-string">"0.01"</span> value<span class="hljs-operator">=</span>{formParams.price} onChange<span class="hljs-operator">=</span>{e <span class="hljs-operator">=</span><span class="hljs-operator">></span> updateFormParams({ ...formParams, price: e.target.<span class="hljs-built_in">value</span> })}<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>input<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>label className<span class="hljs-operator">=</span><span class="hljs-string">"block text-purple-500 text-sm font-bold mb-2"</span> htmlFor<span class="hljs-operator">=</span><span class="hljs-string">"image"</span><span class="hljs-operator">></span>Upload Image<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>label<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>input <span class="hljs-keyword">type</span><span class="hljs-operator">=</span>{<span class="hljs-string">"file"</span>} onChange<span class="hljs-operator">=</span>{<span class="hljs-string">""</span>}<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>input<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>br<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>br<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"text-green text-center"</span><span class="hljs-operator">></span>{message}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>button onClick<span class="hljs-operator">=</span>{<span class="hljs-string">""</span>} className<span class="hljs-operator">=</span><span class="hljs-string">"font-bold mt-10 w-full bg-purple-500 text-white rounded p-2 shadow-lg"</span><span class="hljs-operator">></span>
            List NFT
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>form<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  )
}
</code></pre><p>src/components/Marketplace.js：</p><pre data-type="codeBlock" text="import Navbar from &quot;./Navbar&quot;;
import NFTTile from &quot;./NFTTile&quot;;
import MarketplaceJSON from &quot;../Marketplace.json&quot;;
import axios from &quot;axios&quot;;
import { useState } from &quot;react&quot;;

export default function Marketplace() {
  const sampleData = [
    {
      &quot;name&quot;: &quot;NFT#1&quot;,
      &quot;description&quot;: &quot;Alchemy&apos;s First NFT&quot;,
      &quot;website&quot;: &quot;http://axieinfinity.io&quot;,
      &quot;image&quot;: &quot;https://gateway.pinata.cloud/ipfs/QmTsRJX7r5gyubjkdmzFrKQhHv74p5wT9LdeF1m3RTqrE5&quot;,
      &quot;price&quot;: &quot;0.03ETH&quot;,
      &quot;currentlySelling&quot;: &quot;True&quot;,
      &quot;address&quot;: &quot;0xe81Bf5A757CB4f7F82a2F23b1e59bE45c33c5b13&quot;,
    },
    {
      &quot;name&quot;: &quot;NFT#2&quot;,
      &quot;description&quot;: &quot;Alchemy&apos;s Second NFT&quot;,
      &quot;website&quot;: &quot;http://axieinfinity.io&quot;,
      &quot;image&quot;: &quot;https://gateway.pinata.cloud/ipfs/QmdhoL9K8my2vi3fej97foiqGmJ389SMs55oC5EdkrxF2M&quot;,
      &quot;price&quot;: &quot;0.03ETH&quot;,
      &quot;currentlySelling&quot;: &quot;True&quot;,
      &quot;address&quot;: &quot;0xe81Bf5A757C4f7F82a2F23b1e59bE45c33c5b13&quot;,
    },
    {
      &quot;name&quot;: &quot;NFT#3&quot;,
      &quot;description&quot;: &quot;Alchemy&apos;s Third NFT&quot;,
      &quot;website&quot;: &quot;http://axieinfinity.io&quot;,
      &quot;image&quot;: &quot;https://gateway.pinata.cloud/ipfs/QmTsRJX7r5gyubjkdmzFrKQhHv74p5wT9LdeF1m3RTqrE5&quot;,
      &quot;price&quot;: &quot;0.03ETH&quot;,
      &quot;currentlySelling&quot;: &quot;True&quot;,
      &quot;address&quot;: &quot;0xe81Bf5A757C4f7F82a2F23b1e59bE45c33c5b13&quot;,
    },
  ];
  const [data, updateData] = useState(sampleData);
  const [dataFetched, updateFetched] = useState(false);
async function getAllNFTs() {
    const ethers = require(&quot;ethers&quot;);
    //After adding your Hardhat network to your metamask, this code will get providers and signers
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    //Pull the deployed contract instance
    let contract = new ethers.Contract(MarketplaceJSON.address, MarketplaceJSON.abi, signer)
    //create an NFT Token
    let transaction = await contract.getAllNFTs()

    //Fetch all the details of every NFT from the contract and display
    const items = await Promise.all(transaction.map(async i =&gt; {
        const tokenURI = await contract.tokenURI(i.tokenId);
        let meta = await axios.get(tokenURI);
        meta = meta.data;

        let price = ethers.utils.formatUnits(i.price.toString(), &apos;ether&apos;);
        let item = {
            price,
            tokenId: i.tokenId.toNumber(),
            seller: i.seller,
            owner: i.owner,
            image: meta.image,
            name: meta.name,
            description: meta.description,
        }
        return item;
    }))

    updateFetched(true);
    updateData(items);
}

if(!dataFetched)
    getAllNFTs();

  return (
    &lt;div&gt;
      &lt;Navbar&gt;&lt;/Navbar&gt;
      &lt;div className=&quot;flex flex-col place-items-center mt-20&quot;&gt;
        &lt;div className=&quot;md:text-xl font-bold text-white&quot;&gt;
          Top NFTs
        &lt;/div&gt;
        &lt;div className=&quot;flex mt-5 justify-between flex-wrap max-w-screen-xl text-center&quot;&gt;
          {data.map((value, index) =&gt; {
            return &lt;NFTTile data={value} key={index}&gt;&lt;/NFTTile&gt;;
          })}
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );

}
"><code><span class="hljs-keyword">import</span> <span class="hljs-title class_">Navbar</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"./Navbar"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title class_">NFTTile</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"./NFTTile"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title class_">MarketplaceJSON</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"../Marketplace.json"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Marketplace</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> sampleData = [
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"NFT#1"</span>,
      <span class="hljs-string">"description"</span>: <span class="hljs-string">"Alchemy's First NFT"</span>,
      <span class="hljs-string">"website"</span>: <span class="hljs-string">"http://axieinfinity.io"</span>,
      <span class="hljs-string">"image"</span>: <span class="hljs-string">"https://gateway.pinata.cloud/ipfs/QmTsRJX7r5gyubjkdmzFrKQhHv74p5wT9LdeF1m3RTqrE5"</span>,
      <span class="hljs-string">"price"</span>: <span class="hljs-string">"0.03ETH"</span>,
      <span class="hljs-string">"currentlySelling"</span>: <span class="hljs-string">"True"</span>,
      <span class="hljs-string">"address"</span>: <span class="hljs-string">"0xe81Bf5A757CB4f7F82a2F23b1e59bE45c33c5b13"</span>,
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"NFT#2"</span>,
      <span class="hljs-string">"description"</span>: <span class="hljs-string">"Alchemy's Second NFT"</span>,
      <span class="hljs-string">"website"</span>: <span class="hljs-string">"http://axieinfinity.io"</span>,
      <span class="hljs-string">"image"</span>: <span class="hljs-string">"https://gateway.pinata.cloud/ipfs/QmdhoL9K8my2vi3fej97foiqGmJ389SMs55oC5EdkrxF2M"</span>,
      <span class="hljs-string">"price"</span>: <span class="hljs-string">"0.03ETH"</span>,
      <span class="hljs-string">"currentlySelling"</span>: <span class="hljs-string">"True"</span>,
      <span class="hljs-string">"address"</span>: <span class="hljs-string">"0xe81Bf5A757C4f7F82a2F23b1e59bE45c33c5b13"</span>,
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"NFT#3"</span>,
      <span class="hljs-string">"description"</span>: <span class="hljs-string">"Alchemy's Third NFT"</span>,
      <span class="hljs-string">"website"</span>: <span class="hljs-string">"http://axieinfinity.io"</span>,
      <span class="hljs-string">"image"</span>: <span class="hljs-string">"https://gateway.pinata.cloud/ipfs/QmTsRJX7r5gyubjkdmzFrKQhHv74p5wT9LdeF1m3RTqrE5"</span>,
      <span class="hljs-string">"price"</span>: <span class="hljs-string">"0.03ETH"</span>,
      <span class="hljs-string">"currentlySelling"</span>: <span class="hljs-string">"True"</span>,
      <span class="hljs-string">"address"</span>: <span class="hljs-string">"0xe81Bf5A757C4f7F82a2F23b1e59bE45c33c5b13"</span>,
    },
  ];
  <span class="hljs-keyword">const</span> [data, updateData] = <span class="hljs-title function_">useState</span>(sampleData);
  <span class="hljs-keyword">const</span> [dataFetched, updateFetched] = <span class="hljs-title function_">useState</span>(<span class="hljs-literal">false</span>);
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getAllNFTs</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">const</span> ethers = <span class="hljs-built_in">require</span>(<span class="hljs-string">"ethers"</span>);
    <span class="hljs-comment">//After adding your Hardhat network to your metamask, this code will get providers and signers</span>
    <span class="hljs-keyword">const</span> provider = <span class="hljs-keyword">new</span> ethers.<span class="hljs-property">providers</span>.<span class="hljs-title class_">Web3Provider</span>(<span class="hljs-variable language_">window</span>.<span class="hljs-property">ethereum</span>);
    <span class="hljs-keyword">const</span> signer = provider.<span class="hljs-title function_">getSigner</span>();
    <span class="hljs-comment">//Pull the deployed contract instance</span>
    <span class="hljs-keyword">let</span> contract = <span class="hljs-keyword">new</span> ethers.<span class="hljs-title class_">Contract</span>(<span class="hljs-title class_">MarketplaceJSON</span>.<span class="hljs-property">address</span>, <span class="hljs-title class_">MarketplaceJSON</span>.<span class="hljs-property">abi</span>, signer)
    <span class="hljs-comment">//create an NFT Token</span>
    <span class="hljs-keyword">let</span> transaction = <span class="hljs-keyword">await</span> contract.<span class="hljs-title function_">getAllNFTs</span>()

    <span class="hljs-comment">//Fetch all the details of every NFT from the contract and display</span>
    <span class="hljs-keyword">const</span> items = <span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>(transaction.<span class="hljs-title function_">map</span>(<span class="hljs-keyword">async</span> i => {
        <span class="hljs-keyword">const</span> tokenURI = <span class="hljs-keyword">await</span> contract.<span class="hljs-title function_">tokenURI</span>(i.<span class="hljs-property">tokenId</span>);
        <span class="hljs-keyword">let</span> meta = <span class="hljs-keyword">await</span> axios.<span class="hljs-title function_">get</span>(tokenURI);
        meta = meta.<span class="hljs-property">data</span>;

        <span class="hljs-keyword">let</span> price = ethers.<span class="hljs-property">utils</span>.<span class="hljs-title function_">formatUnits</span>(i.<span class="hljs-property">price</span>.<span class="hljs-title function_">toString</span>(), <span class="hljs-string">'ether'</span>);
        <span class="hljs-keyword">let</span> item = {
            price,
            <span class="hljs-attr">tokenId</span>: i.<span class="hljs-property">tokenId</span>.<span class="hljs-title function_">toNumber</span>(),
            <span class="hljs-attr">seller</span>: i.<span class="hljs-property">seller</span>,
            <span class="hljs-attr">owner</span>: i.<span class="hljs-property">owner</span>,
            <span class="hljs-attr">image</span>: meta.<span class="hljs-property">image</span>,
            <span class="hljs-attr">name</span>: meta.<span class="hljs-property">name</span>,
            <span class="hljs-attr">description</span>: meta.<span class="hljs-property">description</span>,
        }
        <span class="hljs-keyword">return</span> item;
    }))

    <span class="hljs-title function_">updateFetched</span>(<span class="hljs-literal">true</span>);
    <span class="hljs-title function_">updateData</span>(items);
}

<span class="hljs-keyword">if</span>(!dataFetched)
    <span class="hljs-title function_">getAllNFTs</span>();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">Navbar</span>></span><span class="hljs-tag">&#x3C;/<span class="hljs-name">Navbar</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col place-items-center mt-20"</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"md:text-xl font-bold text-white"</span>></span>
          Top NFTs
        <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex mt-5 justify-between flex-wrap max-w-screen-xl text-center"</span>></span>
          {data.map((value, index) => {
            return <span class="hljs-tag">&#x3C;<span class="hljs-name">NFTTile</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{value}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>></span><span class="hljs-tag">&#x3C;/<span class="hljs-name">NFTTile</span>></span>;
          })}
        <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
      <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span></span>
  );

}
</code></pre><h4 id="h-srccomponentsprofilejs" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">src/components/Profile.js：</h4><pre data-type="codeBlock" text="import Navbar from &quot;./Navbar&quot;;
import { useLocation, useParams } from &apos;react-router-dom&apos;;
import MarketplaceJSON from &quot;../Marketplace.json&quot;;
import axios from &quot;axios&quot;;
import { useState } from &quot;react&quot;;
import NFTTile from &quot;./NFTTile&quot;;

export default function Profile() {
  const [data, updateData] = useState([]);
  const [address, updateAddress] = useState(&quot;0x&quot;);
  const [totalPrice, updateTotalPrice] = useState(&quot;0&quot;);
  const [dataFetched, updateFetched] = useState(false);
  async function getNFTData(tokenId) {
    const ethers = require(&quot;ethers&quot;);
    let sumPrice = 0;

    //After adding your Hardhat network to your metamask, this code will get providers and signers
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const addr = await signer.getAddress();

    //Pull the deployed contract instance
    let contract = new ethers.Contract(MarketplaceJSON.address, MarketplaceJSON.abi, signer)

    //create an NFT Token
    let transaction = await contract.getMyNFTs()

    /*
    * Below function takes the metadata from tokenURI and the data returned by getMyNFTs() contract function
    * and creates an object of information that is to be displayed
    */

    const items = await Promise.all(transaction.map(async i =&gt; {
      const tokenURI = await contract.tokenURI(i.tokenId);
      let meta = await axios.get(tokenURI);
      meta = meta.data;

      let price = ethers.utils.formatUnits(i.price.toString(), &apos;ether&apos;);
      let item = {
        price,
        tokenId: i.tokenId.toNumber(),
        seller: i.seller,
        owner: i.owner,
        image: meta.image,
        name: meta.name,
        description: meta.description,
      }
      sumPrice += Number(price);
      return item;
    }))

    updateData(items);
    updateFetched(true);
    updateAddress(addr);
    updateTotalPrice(sumPrice.toPrecision(3));
  }

  const params = useParams();
  const tokenId = params.tokenId;
  if (!dataFetched)
    getNFTData(tokenId);
  return (
    &lt;div className=&quot;profileClass&quot; style={{ &quot;min-height&quot;: &quot;100vh&quot; }}&gt;
      &lt;Navbar&gt;&lt;/Navbar&gt;
      &lt;div className=&quot;profileClass&quot;&gt;
        &lt;div className=&quot;flex text-center flex-col mt-11 md:text-2xl text-white&quot;&gt;
          &lt;div className=&quot;mb-5&quot;&gt;
            &lt;h2 className=&quot;font-bold&quot;&gt;Wallet Address&lt;/h2&gt;
            {address}
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div className=&quot;flex flex-row text-center justify-center mt-10 md:text-2xl text-white&quot;&gt;
          &lt;div&gt;
            &lt;h2 className=&quot;font-bold&quot;&gt;No. of NFTs&lt;/h2&gt;
            {data.length}
          &lt;/div&gt;
          &lt;div className=&quot;ml-20&quot;&gt;
            &lt;h2 className=&quot;font-bold&quot;&gt;Total Value&lt;/h2&gt;
            {totalPrice} ETH
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div className=&quot;flex flex-col text-center items-center mt-11 text-white&quot;&gt;
          &lt;h2 className=&quot;font-bold&quot;&gt;Your NFTs&lt;/h2&gt;
          &lt;div className=&quot;flex justify-center flex-wrap max-w-screen-xl&quot;&gt;
            {data.map((value, index) =&gt; {
              return &lt;NFTTile data={value} key={index}&gt;&lt;/NFTTile&gt;;
            })}
          &lt;/div&gt;
          &lt;div className=&quot;mt-10 text-xl&quot;&gt;
            {data.length == 0 ? &quot;Oops, No NFT data to display (Are you logged in?)&quot; : &quot;&quot;}
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  )
};
"><code><span class="hljs-keyword">import</span> <span class="hljs-title class_">Navbar</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"./Navbar"</span>;
<span class="hljs-keyword">import</span> { useLocation, useParams } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title class_">MarketplaceJSON</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"../Marketplace.json"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title class_">NFTTile</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"./NFTTile"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Profile</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [data, updateData] = <span class="hljs-title function_">useState</span>([]);
  <span class="hljs-keyword">const</span> [address, updateAddress] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">"0x"</span>);
  <span class="hljs-keyword">const</span> [totalPrice, updateTotalPrice] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">"0"</span>);
  <span class="hljs-keyword">const</span> [dataFetched, updateFetched] = <span class="hljs-title function_">useState</span>(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getNFTData</span>(<span class="hljs-params">tokenId</span>) {
    <span class="hljs-keyword">const</span> ethers = <span class="hljs-built_in">require</span>(<span class="hljs-string">"ethers"</span>);
    <span class="hljs-keyword">let</span> sumPrice = <span class="hljs-number">0</span>;

    <span class="hljs-comment">//After adding your Hardhat network to your metamask, this code will get providers and signers</span>
    <span class="hljs-keyword">const</span> provider = <span class="hljs-keyword">new</span> ethers.<span class="hljs-property">providers</span>.<span class="hljs-title class_">Web3Provider</span>(<span class="hljs-variable language_">window</span>.<span class="hljs-property">ethereum</span>);
    <span class="hljs-keyword">const</span> signer = provider.<span class="hljs-title function_">getSigner</span>();
    <span class="hljs-keyword">const</span> addr = <span class="hljs-keyword">await</span> signer.<span class="hljs-title function_">getAddress</span>();

    <span class="hljs-comment">//Pull the deployed contract instance</span>
    <span class="hljs-keyword">let</span> contract = <span class="hljs-keyword">new</span> ethers.<span class="hljs-title class_">Contract</span>(<span class="hljs-title class_">MarketplaceJSON</span>.<span class="hljs-property">address</span>, <span class="hljs-title class_">MarketplaceJSON</span>.<span class="hljs-property">abi</span>, signer)

    <span class="hljs-comment">//create an NFT Token</span>
    <span class="hljs-keyword">let</span> transaction = <span class="hljs-keyword">await</span> contract.<span class="hljs-title function_">getMyNFTs</span>()

    <span class="hljs-comment">/*
    * Below function takes the metadata from tokenURI and the data returned by getMyNFTs() contract function
    * and creates an object of information that is to be displayed
    */</span>

    <span class="hljs-keyword">const</span> items = <span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>(transaction.<span class="hljs-title function_">map</span>(<span class="hljs-keyword">async</span> i => {
      <span class="hljs-keyword">const</span> tokenURI = <span class="hljs-keyword">await</span> contract.<span class="hljs-title function_">tokenURI</span>(i.<span class="hljs-property">tokenId</span>);
      <span class="hljs-keyword">let</span> meta = <span class="hljs-keyword">await</span> axios.<span class="hljs-title function_">get</span>(tokenURI);
      meta = meta.<span class="hljs-property">data</span>;

      <span class="hljs-keyword">let</span> price = ethers.<span class="hljs-property">utils</span>.<span class="hljs-title function_">formatUnits</span>(i.<span class="hljs-property">price</span>.<span class="hljs-title function_">toString</span>(), <span class="hljs-string">'ether'</span>);
      <span class="hljs-keyword">let</span> item = {
        price,
        <span class="hljs-attr">tokenId</span>: i.<span class="hljs-property">tokenId</span>.<span class="hljs-title function_">toNumber</span>(),
        <span class="hljs-attr">seller</span>: i.<span class="hljs-property">seller</span>,
        <span class="hljs-attr">owner</span>: i.<span class="hljs-property">owner</span>,
        <span class="hljs-attr">image</span>: meta.<span class="hljs-property">image</span>,
        <span class="hljs-attr">name</span>: meta.<span class="hljs-property">name</span>,
        <span class="hljs-attr">description</span>: meta.<span class="hljs-property">description</span>,
      }
      sumPrice += <span class="hljs-title class_">Number</span>(price);
      <span class="hljs-keyword">return</span> item;
    }))

    <span class="hljs-title function_">updateData</span>(items);
    <span class="hljs-title function_">updateFetched</span>(<span class="hljs-literal">true</span>);
    <span class="hljs-title function_">updateAddress</span>(addr);
    <span class="hljs-title function_">updateTotalPrice</span>(sumPrice.<span class="hljs-title function_">toPrecision</span>(<span class="hljs-number">3</span>));
  }

  <span class="hljs-keyword">const</span> params = <span class="hljs-title function_">useParams</span>();
  <span class="hljs-keyword">const</span> tokenId = params.<span class="hljs-property">tokenId</span>;
  <span class="hljs-keyword">if</span> (!dataFetched)
    <span class="hljs-title function_">getNFTData</span>(tokenId);
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"profileClass"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> "<span class="hljs-attr">min-height</span>"<span class="hljs-attr">:</span> "<span class="hljs-attr">100vh</span>" }}></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">Navbar</span>></span><span class="hljs-tag">&#x3C;/<span class="hljs-name">Navbar</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"profileClass"</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex text-center flex-col mt-11 md:text-2xl text-white"</span>></span>
          <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-5"</span>></span>
            <span class="hljs-tag">&#x3C;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold"</span>></span>Wallet Address<span class="hljs-tag">&#x3C;/<span class="hljs-name">h2</span>></span>
            {address}
          <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
        <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-row text-center justify-center mt-10 md:text-2xl text-white"</span>></span>
          <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
            <span class="hljs-tag">&#x3C;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold"</span>></span>No. of NFTs<span class="hljs-tag">&#x3C;/<span class="hljs-name">h2</span>></span>
            {data.length}
          <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
          <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"ml-20"</span>></span>
            <span class="hljs-tag">&#x3C;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold"</span>></span>Total Value<span class="hljs-tag">&#x3C;/<span class="hljs-name">h2</span>></span>
            {totalPrice} ETH
          <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
        <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col text-center items-center mt-11 text-white"</span>></span>
          <span class="hljs-tag">&#x3C;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold"</span>></span>Your NFTs<span class="hljs-tag">&#x3C;/<span class="hljs-name">h2</span>></span>
          <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-center flex-wrap max-w-screen-xl"</span>></span>
            {data.map((value, index) => {
              return <span class="hljs-tag">&#x3C;<span class="hljs-name">NFTTile</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{value}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>></span><span class="hljs-tag">&#x3C;/<span class="hljs-name">NFTTile</span>></span>;
            })}
          <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
          <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-10 text-xl"</span>></span>
            {data.length == 0 ? "Oops, No NFT data to display (Are you logged in?)" : ""}
          <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
        <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
      <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span></span>
  )
};
</code></pre><h4 id="h-srccomponentsnftpagejs" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">src/components/NFTPage.js</h4><pre data-type="codeBlock" text="import Navbar from &quot;./Navbar&quot;;
import axie from &quot;../tile.jpeg&quot;;
import { useLocation, useParams } from &apos;react-router-dom&apos;;
import MarketplaceJSON from &quot;../Marketplace.json&quot;;
import axios from &quot;axios&quot;;
import { useState } from &quot;react&quot;;

export default function NFTPage(props) {

  const [data, updateData] = useState({});
  const [message, updateMessage] = useState(&quot;&quot;);
  const [currAddress, updateCurrAddress] = useState(&quot;0x&quot;);
  const [dataFetched, updateDataFetched] = useState(false);
  async function getNFTData(tokenId) {
    const ethers = require(&quot;ethers&quot;);
    //After adding your Hardhat network to your metamask, this code will get providers and signers
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    //Pull the deployed contract instance
    let contract = new ethers.Contract(MarketplaceJSON.address, MarketplaceJSON.abi, signer)
    //create an NFT Token
    const tokenURI = await contract.tokenURI(tokenId);
    const listedToken = await contract.getListedTokenForId(tokenId);
    let meta = await axios.get(tokenURI);
    meta = meta.data;
    console.log(listedToken);

    let item = {
      price: meta.price,
      tokenId: tokenId,
      seller: listedToken.seller,
      owner: listedToken.owner,
      image: meta.image,
      name: meta.name,
      description: meta.description,
    }
    console.log(item);
    updateData(item);
    updateDataFetched(true);
  }

  async function buyNFT(tokenId) {
    try {
      const ethers = require(&quot;ethers&quot;);
      //After adding your Hardhat network to your metamask, this code will get providers and signers
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      //Pull the deployed contract instance
      let contract = new ethers.Contract(MarketplaceJSON.address, MarketplaceJSON.abi, signer);
      const salePrice = ethers.utils.parseUnits(data.price, &apos;ether&apos;)
      let transaction = await contract.executeSale(tokenId, { value: salePrice });
      await transaction.wait();

      alert(&apos;You successfully bought the NFT!&apos;);
    }
    catch (e) {
      alert(&quot;Upload Error&quot; + e)
    }
  }

  return (
    &lt;div style={{ &quot;min-height&quot;: &quot;100vh&quot; }}&gt;
      &lt;Navbar&gt;&lt;/Navbar&gt;
      &lt;div className=&quot;flex ml-20 mt-20&quot;&gt;
        &lt;img src={data.image} alt=&quot;&quot; className=&quot;w-2/5&quot; /&gt;
        &lt;div className=&quot;text-xl ml-20 space-y-8 text-white shadow-2xl rounded-lg border-2 p-5&quot;&gt;
          &lt;div&gt;
            Name: {data.name}
          &lt;/div&gt;
          &lt;div&gt;
            Description: {data.description}
          &lt;/div&gt;
          &lt;div&gt;
            Price: &lt;span className=&quot;&quot;&gt;{data.price + &quot; ETH&quot;}&lt;/span&gt;
          &lt;/div&gt;
          &lt;div&gt;
            Owner: &lt;span className=&quot;text-sm&quot;&gt;{data.owner}&lt;/span&gt;
          &lt;/div&gt;
          &lt;div&gt;
            Seller: &lt;span className=&quot;text-sm&quot;&gt;{data.seller}&lt;/span&gt;
          &lt;/div&gt;
          &lt;div&gt;
            {currAddress == data.owner || currAddress == data.seller ?
              &lt;div className=&quot;text-emerald-700&quot;&gt;You are the owner of this NFT&lt;/div&gt;
              : &lt;button className=&quot;enableEthereumButton bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded text-sm&quot;&gt;Buy this NFT&lt;/button&gt;
            }

            &lt;div className=&quot;text-green text-center mt-3&quot;&gt;{message}&lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  )
}
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">Navbar</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./Navbar"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">axie</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../tile.jpeg"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useLocation</span>, <span class="hljs-title">useParams</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'react-router-dom'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">MarketplaceJSON</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../Marketplace.json"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">axios</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NFTPage</span>(<span class="hljs-params">props</span>) </span>{

  const [data, updateData] <span class="hljs-operator">=</span> useState({});
  const [message, updateMessage] <span class="hljs-operator">=</span> useState(<span class="hljs-string">""</span>);
  const [currAddress, updateCurrAddress] <span class="hljs-operator">=</span> useState(<span class="hljs-string">"0x"</span>);
  const [dataFetched, updateDataFetched] <span class="hljs-operator">=</span> useState(<span class="hljs-literal">false</span>);
  async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getNFTData</span>(<span class="hljs-params">tokenId</span>) </span>{
    const ethers <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"ethers"</span>);
    <span class="hljs-comment">//After adding your Hardhat network to your metamask, this code will get providers and signers</span>
    const provider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(window.ethereum);
    const signer <span class="hljs-operator">=</span> provider.getSigner();
    <span class="hljs-comment">//Pull the deployed contract instance</span>
    let <span class="hljs-class"><span class="hljs-keyword">contract</span> = <span class="hljs-title"><span class="hljs-keyword">new</span></span> <span class="hljs-title">ethers</span>.<span class="hljs-title">Contract</span>(<span class="hljs-params">MarketplaceJSON.<span class="hljs-keyword">address</span>, MarketplaceJSON.<span class="hljs-built_in">abi</span>, signer</span>)
    <span class="hljs-comment">//create an NFT Token</span>
    <span class="hljs-title">const</span> <span class="hljs-title">tokenURI</span> = <span class="hljs-title">await</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">tokenURI</span>(<span class="hljs-params">tokenId</span>);
    <span class="hljs-title">const</span> <span class="hljs-title">listedToken</span> = <span class="hljs-title">await</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">getListedTokenForId</span>(<span class="hljs-params">tokenId</span>);
    <span class="hljs-title">let</span> <span class="hljs-title">meta</span> = <span class="hljs-title">await</span> <span class="hljs-title">axios</span>.<span class="hljs-title">get</span>(<span class="hljs-params">tokenURI</span>);
    <span class="hljs-title">meta</span> = <span class="hljs-title">meta</span>.<span class="hljs-title">data</span>;
    <span class="hljs-title">console</span>.<span class="hljs-title">log</span>(<span class="hljs-params">listedToken</span>);

    <span class="hljs-title">let</span> <span class="hljs-title">item</span> = </span>{
      price: meta.price,
      tokenId: tokenId,
      seller: listedToken.seller,
      owner: listedToken.owner,
      image: meta.image,
      name: meta.<span class="hljs-built_in">name</span>,
      description: meta.description,
    }
    console.log(item);
    updateData(item);
    updateDataFetched(<span class="hljs-literal">true</span>);
  }

  async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buyNFT</span>(<span class="hljs-params">tokenId</span>) </span>{
    <span class="hljs-keyword">try</span> {
      const ethers <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"ethers"</span>);
      <span class="hljs-comment">//After adding your Hardhat network to your metamask, this code will get providers and signers</span>
      const provider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(window.ethereum);
      const signer <span class="hljs-operator">=</span> provider.getSigner();
      <span class="hljs-comment">//Pull the deployed contract instance</span>
      let <span class="hljs-class"><span class="hljs-keyword">contract</span> = <span class="hljs-title"><span class="hljs-keyword">new</span></span> <span class="hljs-title">ethers</span>.<span class="hljs-title">Contract</span>(<span class="hljs-params">MarketplaceJSON.<span class="hljs-keyword">address</span>, MarketplaceJSON.<span class="hljs-built_in">abi</span>, signer</span>);
      <span class="hljs-title">const</span> <span class="hljs-title">salePrice</span> = <span class="hljs-title">ethers</span>.<span class="hljs-title">utils</span>.<span class="hljs-title">parseUnits</span>(<span class="hljs-params">data.price, <span class="hljs-string">'ether'</span></span>)
      <span class="hljs-title">let</span> <span class="hljs-title">transaction</span> = <span class="hljs-title">await</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">executeSale</span>(<span class="hljs-params">tokenId, { value: salePrice }</span>);
      <span class="hljs-title">await</span> <span class="hljs-title">transaction</span>.<span class="hljs-title">wait</span>(<span class="hljs-params"></span>);

      <span class="hljs-title">alert</span>(<span class="hljs-params"><span class="hljs-string">'You successfully bought the NFT!'</span></span>);
    }
    <span class="hljs-title"><span class="hljs-keyword">catch</span></span> (<span class="hljs-params">e</span>) </span>{
      alert(<span class="hljs-string">"Upload Error"</span> <span class="hljs-operator">+</span> e)
    }
  }

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ <span class="hljs-string">"min-height"</span>: <span class="hljs-string">"100vh"</span> }}<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>Navbar<span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Navbar<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex ml-20 mt-20"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>img src<span class="hljs-operator">=</span>{data.image} alt<span class="hljs-operator">=</span><span class="hljs-string">""</span> className<span class="hljs-operator">=</span><span class="hljs-string">"w-2/5"</span> <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"text-xl ml-20 space-y-8 text-white shadow-2xl rounded-lg border-2 p-5"</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
            Name: {data.<span class="hljs-built_in">name</span>}
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
            Description: {data.description}
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
            Price: <span class="hljs-operator">&#x3C;</span>span className<span class="hljs-operator">=</span><span class="hljs-string">""</span><span class="hljs-operator">></span>{data.price <span class="hljs-operator">+</span> <span class="hljs-string">" ETH"</span>}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>span<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
            Owner: <span class="hljs-operator">&#x3C;</span>span className<span class="hljs-operator">=</span><span class="hljs-string">"text-sm"</span><span class="hljs-operator">></span>{data.owner}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>span<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
            Seller: <span class="hljs-operator">&#x3C;</span>span className<span class="hljs-operator">=</span><span class="hljs-string">"text-sm"</span><span class="hljs-operator">></span>{data.seller}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>span<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
            {currAddress <span class="hljs-operator">=</span><span class="hljs-operator">=</span> data.owner <span class="hljs-operator">|</span><span class="hljs-operator">|</span> currAddress <span class="hljs-operator">=</span><span class="hljs-operator">=</span> data.seller ?
              <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"text-emerald-700"</span><span class="hljs-operator">></span>You are the owner of <span class="hljs-built_in">this</span> NFT<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              : <span class="hljs-operator">&#x3C;</span>button className<span class="hljs-operator">=</span><span class="hljs-string">"enableEthereumButton bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded text-sm"</span><span class="hljs-operator">></span>Buy <span class="hljs-built_in">this</span> NFT<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
            }

            <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"text-green text-center mt-3"</span><span class="hljs-operator">></span>{message}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  )
}
</code></pre><h2 id="h-7" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">7.测试代码</h2><p>我们在shell输入npm start 则会出现这样的页面</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/511bb5e996725cb31ae5ed1df61d12b7e34cfb985ac6570e0f59eafa588f7d81.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5b0227b3d7fb414d6cb8a7c43c9251d43d2cd055ab06e6899cc186eebb83fdfc.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>monkey-11@newsletter.paragraph.com (Monkey)</author>
        </item>
    </channel>
</rss>