<?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>Elsa</title>
        <link>https://paragraph.com/@elsa-2</link>
        <description>undefined</description>
        <lastBuildDate>Wed, 22 Apr 2026 23:35:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Elsa</title>
            <url>https://storage.googleapis.com/papyrus_images/1faa0c3494d683849e09e88e1f79386239b0bb582448f7783159a94b6025c15f.gif</url>
            <link>https://paragraph.com/@elsa-2</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[面对“黑天鹅”，我们如何处理？]]></title>
            <link>https://paragraph.com/@elsa-2/QqV6qxhK4gogDlKd1aFP</link>
            <guid>QqV6qxhK4gogDlKd1aFP</guid>
            <pubDate>Sun, 02 Apr 2023 05:05:01 GMT</pubDate>
            <description><![CDATA[这次由硅谷银行的暴雷引发的稳定币全面、系统性的脱锚无论如何属于“黑天鹅”事件了。 此前我还认为在FTX暴雷后，今年的加密生态即便再发生重大事故我们也都猜得到主角是谁，因此不算再有“黑天鹅”。可无论如何我猜不到这才没过多久，稳定币竟然脱锚了。 那么发生这些“黑天鹅”事件时，我们都有哪些方法可以保护自己呢？ 我自己会尽量先在第一时间弄清事件的原委，分析清楚到底这个事件是不可逆转的还是只是意外的一次波动。如果是不可逆转的，那我会毫不犹豫地尽快让自己先离开这些风险。如果只是一次意外的波动，我就会继续观察。 以这次稳定币脱锚的事件为例，当USDC出现问题并牵连DAI时，由于我持有的都是DAI，并且认为DAI的脱锚只是暂时的，因此我没有进行任何操作，只是继续观察。 可假如USDC的价格降到0.8美元甚至以下，并且在这个价位保持了几个小时依旧上不来时，那我就会首先考虑尽快把DAI换成法币。如果没有这个渠道，那我就会把DAI抛掉，分散买入LUSD、以太坊和比特币。 在第一方案中，我之所以会先考虑把DAI换成法币（美元或者人民币）是因为我还是想继续留存“稳定币”，但是想找个更“安全”的稳定币。所以...]]></description>
            <content:encoded><![CDATA[<p>这次由硅谷银行的暴雷引发的稳定币全面、系统性的脱锚无论如何属于“黑天鹅”事件了。</p><p>此前我还认为在FTX暴雷后，今年的加密生态即便再发生重大事故我们也都猜得到主角是谁，因此不算再有“黑天鹅”。可无论如何我猜不到这才没过多久，稳定币竟然脱锚了。</p><p>那么发生这些“黑天鹅”事件时，我们都有哪些方法可以保护自己呢？</p><p>我自己会尽量先在第一时间弄清事件的原委，分析清楚到底这个事件是不可逆转的还是只是意外的一次波动。如果是不可逆转的，那我会毫不犹豫地尽快让自己先离开这些风险。如果只是一次意外的波动，我就会继续观察。</p><p>以这次稳定币脱锚的事件为例，当USDC出现问题并牵连DAI时，由于我持有的都是DAI，并且认为DAI的脱锚只是暂时的，因此我没有进行任何操作，只是继续观察。</p><p>可假如USDC的价格降到0.8美元甚至以下，并且在这个价位保持了几个小时依旧上不来时，那我就会首先考虑尽快把DAI换成法币。如果没有这个渠道，那我就会把DAI抛掉，分散买入LUSD、以太坊和比特币。</p><p>在第一方案中，我之所以会先考虑把DAI换成法币（美元或者人民币）是因为我还是想继续留存“稳定币”，但是想找个更“安全”的稳定币。所以法币就是不二选择-----因为所有的稳定币最终锚定的都是法币。</p><p>在这种情况下，等风险散尽，我才会把法币再换回稳定币。</p><p>在退而求其次的方案中，我之所以在稳定币中只买LUSD是因为它纯粹是用以太坊作抵押，所以它的风险最小。我买入以太坊和比特币是因为我相信它们的未来。</p><p>在这种情况下，等风险散尽，买入的LUSD、以太坊和比特币我就都继续持有不动了。</p><p>除此以外，还有另外一种思路。这种思路认为USDC的脱锚只是暂时的，它终究会回去，因此在这个时候甚至大胆买入。</p><p>这个做法如果合理，至少要满足下面两个条件中的一个：</p><p>一是Circle这家公司有能力最终偿付这笔减记。</p><p>二是假如Circle没有钱偿付这笔减记，美国政府最终会施以援手救助硅谷银行让所有的储户安全脱险。</p><p>对第一个假设条件，我认为在当时那个情况下，我们是很难判别的。</p><p>大家之所以相信Circle还能偿付是因为它存在硅谷银行中的钱只占小部分，而大部分钱都存在其它几家大银行。而对那几家大银行大家默认认为它们是安全的。</p><p>可问题是在已经有几家银行都出问题的情况下，我们怎么知道这个风险不会传导、怎么保证那几家所谓的大银行都安全呢？</p><p>所以第一个假设条件在我看来有点牵强。</p><p>但对第二个假设条件，大家就信心满满了。可以找到一堆理由，其中最有说服力的就是，美国政府不会放纵科技业承受如此打击、还会说在当前美国政府拼命确保其高科技地位的情况下，不会让这样的灾难发生。</p><p>其实在我看来，这个说服力也不是那么强。</p><p>首先这次受影响的科技业和2008年那次受影响的金融业对整个系统的影响是有差别的。金融完蛋那百业就萧条了，而科技受损虽说也是不可承受之重，但暂时而言它的风险和影响是控制在局部领域和一定范围的。</p><p>在这种情况下我们很难确保监管机构就会那么毫不犹豫地施以援手。</p><p>其实即便是在2008年美国政府要救助整个金融行业时，当时的财长保尔森在内心都纠结了许久许久。因为对他而言，他即将作出的行为是违背资本主义市场经济规律的：那是一种深深刻在他骨子里的信条--------从青葱稚嫩的学生时代一直到后来久经历练的职业生涯。</p><p>当时有一张照片极其生动地体现了他内心的纠结和痛苦：他孤独地站在角落里，不和任何人交流、谈话，只是低头沉思。</p><p>而且当时对救不救金融业在全社会都是争论巨大的。</p><p>2008年都如此，何况今日。</p><p>所以在这种情况下，尽管政府会救助硅谷银行的可能性相当大，但我们也绝对不能忽视另外“不救”的可能。</p><p>所以我不会采用这种稍微冒险的做法。</p><p>我一向以来的信条就是：风险控制第一、活下来最重要；追逐利润第二、利润只是锦上添花。</p>]]></content:encoded>
            <author>elsa-2@newsletter.paragraph.com (Elsa)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy第一周边学边获取NFT]]></title>
            <link>https://paragraph.com/@elsa-2/alchemy-nft</link>
            <guid>ty2FqKYTvKS56KjIbBpD</guid>
            <pubDate>Sat, 15 Oct 2022 12:48:34 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(...]]></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>合约编写页面</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>部署合约</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>已经部署完成的合约</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>elsa-2@newsletter.paragraph.com (Elsa)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3-第二周文本教程]]></title>
            <link>https://paragraph.com/@elsa-2/alchemy-the-road-to-web3</link>
            <guid>UVd191OoDynrTjxTSEJB</guid>
            <pubDate>Sat, 15 Oct 2022 11:58:43 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 ethers@...]]></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><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: 0xCDD46957E7F3d28693e5e379A246d478e8939319
== 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">0xCDD46957E7F3d28693e5e379A246d478e8939319</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><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: 0xCDD46957E7F3d28693e5e379A246d478e8939319
"><code>BuyMeACoffee deployed <span class="hljs-selector-tag">to</span>: <span class="hljs-number">0</span>xCDD46957E7F3d28693e5e379A246d478e8939319
</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>项目新的结构</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: 0xCDD46957E7F3d28693e5e379A246d478e8939319
"><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">0xCDD46957E7F3d28693e5e379A246d478e8939319</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><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>]]></content:encoded>
            <author>elsa-2@newsletter.paragraph.com (Elsa)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy第三周任务-使用Polygon链上元数据制作 NFT]]></title>
            <link>https://paragraph.com/@elsa-2/alchemy-polygon-nft</link>
            <guid>kmVYUHs4oIdLwD921cAA</guid>
            <pubDate>Sat, 15 Oct 2022 08:00:07 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 0x33C61D75B3C5471Cc921Bc6005F3449750f0aDCb
"><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 <span class="hljs-number">0x33C61D75B3C5471Cc921Bc6005F3449750f0aDCb</span>
</code></pre><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>elsa-2@newsletter.paragraph.com (Elsa)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy 第四周NFT获取教程]]></title>
            <link>https://paragraph.com/@elsa-2/alchemy-nft-2</link>
            <guid>N7iCASfb5ER5F8dRHR53</guid>
            <pubDate>Sat, 15 Oct 2022 07:50:11 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>elsa-2@newsletter.paragraph.com (Elsa)</author>
        </item>
    </channel>
</rss>