<?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>Robby</title>
        <link>https://paragraph.com/@sheepswap</link>
        <description>undefined</description>
        <lastBuildDate>Sun, 21 Jun 2026 16:50:53 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[zkSync Era 测试网交互教程]]></title>
            <link>https://paragraph.com/@sheepswap/zksync-era</link>
            <guid>lTPCkDQMq1uRKtARTSre</guid>
            <pubDate>Sun, 19 Mar 2023 13:12:49 GMT</pubDate>
            <description><![CDATA[zkSync是一种ZK rollup,一种使用加密有效性证明在以太坊上提供可扩展和低成本交易的无信任协议.在zkSync中,计算是在链下执行的,大多数数据也存储在链下，最重要要的是融资搞了4.58亿美元,投资阵容也灰常牛逼进入领水网站 Goerli Faucet 1.官方桥 右下角切换Goerli测试网,主网项目方才能申请,我们只能参与测试网2.SyncSwap 比较简单就是常规dex操作 swap → 添加流动性 → add或remove流动性3.nexon借贷项目 任意选择一个资产supply:存款 borrow: 借款 repay:还款 withdraw:提现4.mute这个也是dex平台 常规操作 swap → 添加流动性 → add或remove流动性添加流动性从swap购买mute然后进入DAO锁仓5.kreatorland现代化的完全去中心化NFT集合发布平台和市场完成一些简单NFT资料,Mint一个NFT 进入DC丢截图+钱包地址]]></description>
            <content:encoded><![CDATA[<h3 id="h-zksynczk-rollupzksync458yuan" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">zkSync是一种ZK rollup,一种使用加密有效性证明在以太坊上提供可扩展和低成本交易的无信任协议.在zkSync中,计算是在链下执行的,大多数数据也存储在链下，最重要要的是融资搞了4.58亿美元,投资阵容也灰常牛逼</h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c27759bc214f24a5ef27a9bac4435f58bcb24828037b80821b2bdf971d47c365.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> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://goerlifaucet.com/"><em>Goerli Faucet</em></a></p><p>1.<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://goerli.portal.zksync.io/bridge">官方桥</a></p><p>右下角切换Goerli测试网,主网项目方才能申请,我们只能参与测试网</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a49cd001054e999e24758a33acf54f3a2e9e9651906f9f38ed9b4ffead2c90bb.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="https://syncswap.xyz/">SyncSwap</a></p><p>比较简单就是常规dex操作 swap → 添加流动性 → add或remove流动性</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3c3d59fbcb8b2a71ca19cc900faa5847ffb545aa8d4b3f93f81d356c8db4b258.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.<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnet.nexon.finance/">nexon</a>借贷项目</p><p>任意选择一个资产</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/382db17844ffe471ea1620dc1f0c8acfbc36d7f3c1ba64cba50a7030e34ddc05.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>supply:存款 borrow: 借款 repay:还款 withdraw:提现</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0a829de8009d3faa07738e2582debe86f7592808ca446b46106360f7c5a59a2e.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.<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://app.mute.io/">mute</a>这个也是dex平台</p><p>常规操作 swap → 添加流动性 → add或remove流动性</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/cadfee63e82ccc541c7502a2d0c836d052ae9024b9c458cb8e0d1369c4de9b87.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>添加流动性</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ee049703e63585d7f9b0ea28b8c0e22f9e815bb1fd81263859e8580dbd0817e6.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>从swap购买mute然后进入DAO锁仓</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f1fa2dc96b8544a3f88ba74545ab365a8a2e644cdc0b31818434d86be396667b.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>5.<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://kreatorland.com/">kreatorland</a>现代化的完全去中心化NFT集合发布平台和市场</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0e9d7960716163f2937cb211b201cf71c5a2380230fca8125a46ad922cf36e60.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资料,Mint一个NFT</p><p>进入<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/dkHjBQfvbf">DC</a>丢截图+钱包地址</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/332219a561336660934b4412ef475d2582a221e2fa3fa9fa88c250171ed11235.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>sheepswap@newsletter.paragraph.com (Robby)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy 项目Road to Web3第一周NFT获取教程]]></title>
            <link>https://paragraph.com/@sheepswap/alchemy-road-to-web3-nft</link>
            <guid>RgFzW775X6PeBYugwpbU</guid>
            <pubDate>Sun, 25 Sep 2022 14:49:21 GMT</pubDate>
            <description><![CDATA[2019年12月，Alchemy完成1500万美元A轮融资，资方为Pantera Capital，斯坦福大学，Coinbase，三星等。 2021年4月，Alchemy以5.05亿美元估值完成8000万美元B轮融资，Coatue和Addition领投，DFJ Growth、K5 Global、Chainsmokers（烟鬼组合）、演员Jared Leto和Glazer家族参投。 2021年10月，Alchemy以35亿美元估值完成2.5亿美元C轮融资，由a16z领投的。 2022年2月，Alchemy以102亿美元估值完成2亿美元融资，Lightspeed与Silver Lake领投。 Alchemy是一个背景强大、经费充足、踏实做事、没有发币的团队，这样的项目不刷，难道去刷土狗吗？ 并且，Alchemy计划将新资金用于推广Web3采用，这方面的一些举措包括推出Web3 University，就是现在的 Road to Web3 活动，活动为期10周，每周一个NFT。看了下nft数量极少，估计由于任务难度大，很多小伙伴直接放弃，这样的项目若是空投，绝对是大毛。手把手第一周教程开始...]]></description>
            <content:encoded><![CDATA[<p>2019年12月，Alchemy完成<strong>1500万美元A轮融资</strong>，资方为Pantera Capital，斯坦福大学，Coinbase，三星等。</p><p>2021年4月，Alchemy以5.05亿美元估值完成<strong>8000万美元B轮融资</strong>，Coatue和Addition领投，DFJ Growth、K5 Global、Chainsmokers（烟鬼组合）、演员Jared Leto和Glazer家族参投。</p><p>2021年10月，Alchemy以35亿美元估值完成<strong>2.5亿美元C轮融资</strong>，由a16z领投的。</p><p>2022年2月，Alchemy以102亿美元估值完成2亿美元融资，Lightspeed与Silver Lake领投。</p><p><strong>Alchemy是一个</strong>背景强大、经费充足、踏实做事、没有发币的团队，这样的项目不刷，难道去刷土狗吗？</p><p>并且，Alchemy计划将新资金用于推广Web3采用，这方面的一些举措包括推出Web3 University，就是现在的 Road to Web3 活动，活动为期10周，每周一个NFT。看了下nft数量极少，估计由于任务难度大，很多小伙伴直接放弃，这样的项目若是空投，绝对是大毛。</p><h2 id="h-alchemy-nft-erc721" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">手把手第一周教程开始：如何使用 Alchemy 开发 NFT 智能合约（ERC721）</h2><p>官方原版<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.alchemy.com/docs/how-to-develop-an-nft-smart-contract-erc721-with-alchemy">教程链接</a>。</p><h3 id="h-step1-openzeppelin-erc721" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step1 使用 OpenZeppelin 合约向导开发 ERC721 智能合约。</h3><p>1.开发 ERC721 NFT 智能合约需要做的第一件事是 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts/4.x/wizard"><strong>进入 Open Zeppelin 智能合约向导页面。</strong></a></p><p>进入页面后，您将看到以下编辑器：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/47d20439fef4c29331d1d5f8f83c0feaa92e35b227f0ed3ffcfaf07a62d21e91.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.单击左上角的 ERC721 按钮，选择要使用的 ERC 标准类型，输入Name和Symbol：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e38aa799183a4a9f73ec15b2bbfd8ffe9c3002ef2f23a26372804f31d1edbb82.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.如图所示勾选 NFT (ERC721) 代币功能：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5d098b87395a6dc5242e1a1b65ee1a7ea2ddf85724e384a617c83166e9946e5d.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>勾选完毕后右侧代码如下：</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import &quot;@openzeppelin/contracts/token/ERC721/ERC721.sol&quot;;
import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol&quot;;
import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol&quot;;
import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Counters.sol&quot;;

contract Alchemy is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721(&quot;Alchemy&quot;, &quot;ALCH&quot;) {}

    function safeMint(address to, string memory uri) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
"><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.4;</span>

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

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Alchemy</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC721</span>, <span class="hljs-title">ERC721Enumerable</span>, <span class="hljs-title">ERC721URIStorage</span>, <span class="hljs-title">Ownable</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> _tokenIdCounter;

    <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">"Alchemy"</span>, <span class="hljs-string">"ALCH"</span></span>) </span>{}

    <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 class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-keyword">uint256</span> tokenId <span class="hljs-operator">=</span> _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    <span class="hljs-comment">// The following functions are overrides required by Solidity.</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_beforeTokenTransfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>, <span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> tokenId</span>)
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span>
        <span class="hljs-title"><span class="hljs-keyword">override</span></span>(<span class="hljs-params">ERC721, ERC721Enumerable</span>)
    </span>{
        <span class="hljs-built_in">super</span>._beforeTokenTransfer(<span class="hljs-keyword">from</span>, to, tokenId);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_burn</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span>(<span class="hljs-params">ERC721, ERC721URIStorage</span>) </span>{
        <span class="hljs-built_in">super</span>._burn(tokenId);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tokenURI</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>)
        <span class="hljs-title"><span class="hljs-keyword">public</span></span>
        <span class="hljs-title"><span class="hljs-keyword">view</span></span>
        <span class="hljs-title"><span class="hljs-keyword">override</span></span>(<span class="hljs-params">ERC721, ERC721URIStorage</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">return</span> <span class="hljs-built_in">super</span>.tokenURI(tokenId);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">supportsInterface</span>(<span class="hljs-params"><span class="hljs-keyword">bytes4</span> interfaceId</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">override</span></span>(<span class="hljs-params">ERC721, ERC721Enumerable</span>)
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.supportsInterface(interfaceId);
    }
}
</code></pre><h3 id="h-step2-remix-ide-erc721" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step2 使用 REMIX IDE 修改和部署ERC721 合约</h3><p>1.首先，您可能已经注意到，在 OpenZeppelin Wizard 编辑器的顶部，有一个“Open in Remix”按钮，单击它将在浏览器的新选项卡中打开 REMIX IDE。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/de147836408c941dc771e5f62bfbc5648085706f1824f3f754a5002dd3f9a64c.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.删除17行的onlyOwner，否则只允许智能合约的所有者（部署智能合约的钱包地址）铸造 NFT。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/98168c0732ccc93b38a6498bdc53c56a4a66c61f7cd8ac5a5565b9c7601b11f8.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.在第14行填入 uint256 MAX_SUPPLY = 100000;</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9dc90347f9531edaf19e889d851d91b7e40b2777b1f38a7a148d6486f5f5babe.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.在第19行添加 require(_tokenIdCounter.current() &lt;= MAX_SUPPLY, &quot;I&apos;m sorry we reached the cap&quot;);</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/205270b9f8fadc8a3170a67a27f31b0b5f609c1fd46920f012664b5baaad6cba.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-one">alchemy.com </a>点击“登录”并创建一个新帐户：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8800f568ac62afc7fb170d9e461d951961e9ce37ad490ad862c6781d7221e904.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.进入Dashboard，点击create app。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b22d2e3d85ce8fef35fefc39e4dc631176ea2660a2fb455d6f38385f9858e106.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.如图，名字和描述可以随意输入，最下面选择以太链的Rinkeby，点击Create app。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/44d6d2b1354664255d83356060a29aa12a351a2f2a7e16f31d3435ca46a057e5.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.创建完成后，回到仪表板，<strong>单击“VIEW KEY”按钮</strong> ，然后复制 HTTPS URL：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/bcede615a7d4d13a9926e3c52bebb937ac7ed9e86a495c0fc1499c82f946128e.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-alchemy-rinkeby-metamask" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step4 将 Alchemy Rinkeby 添加到 Metamask 钱包</h3><p>1.点击添加网络。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a5861166c2228be22471080226fa807ae4762ee81a763d0b40a67b6c5b05f8f5.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.在其中填写 Rinkeby 网络和 RPC URL 信息并保存。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6b0c61d291318c6cec57e022e5032c919bf3bdf17fbd9e18b1457d13f618da6f.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.获取 Rinkeby Test ETH ,只需导航到 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://rinkebyfaucet.com/">rinkebyfaucet.com </a>，将钱包地址复制到文本栏中，然后点击“Send Me ETH”。10-20 秒后，会看到 Rinkeby ETH 出现在 Metamask 钱包中。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2e56bb4b2e632f72da0bd54c61ef12eb4e0995a7c00583b3d53a9fb5a9052871.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-step-5-rinkeby-nft" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step 5 在 Rinkeby 测试网上编译和部署 NFT 智能合约</h3><p>1.回到 Remix，点击页面左侧的编译器菜单，版本选0.8.4，勾选Auto compile，然后点击“编译”按钮：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4862008abafd0b4f581e76f846bae5f8533edc3f63cefd89c4bea9baeeae427b.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.然后点击“Deploy and Run Transactions”菜单，点击Environment下拉菜单并选择“Injected provider (Metamask)”，点击contract选择Alchemy那个，单击deploy。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7d38a2e80d387d28a84d3a08f7c6503c4efe3b77ea4320ad17d847090e5533e1.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.将出现 Metamask 弹出窗口，单击“确认”，然后继续支付 Gas 费用。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0d620fe1c414ca5f427ae02f2f985f6a75fee020136648ca458b78100bb5bd7d.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.如果一切都按预期工作，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/2c68fd7cba47acd046541e568b290a51574d8a9e7dab085368cfd634f1f37819.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-ipfs-yuan" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step6 在 IPFS 上创建和上传元数据</h3><p>1.首先，到 <code>filebase.com</code>创建一个新帐户。</p><p>登录后，单击左侧菜单上的存储桶按钮，然后创建一个新存储桶(自己取名字，重名不行)：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/908d0dc339803a873892c9848f7785c8ca1f8e0d0ec87905a1a2164f9ebdf22c.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/d5067b460361811df7dca91f27b64c84e43dfd2dd65ef9b43a620dd609c119d2.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.导航到存储桶，单击 <strong>上传按钮</strong> ，然后上传您要用于 NFT 的图像， <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ipfs.filebase.io/ipfs/bafybeihyvhgbcov2nmvbnveunoodokme5eb42uekrqowxdennt2qyeculm">建议使用以下内容 </a>。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b4de5796337e3714634f0783665d719c823e92e5e4f56bd17dc8a986a6bb6b36.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.上传后单击它并复制 IPFS 网关 URL：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9cf361e6a1b809311009ab0a2270c4af19fb9cb4207e710e65b77039ca1f2c83.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.建txt文档，粘贴以下 JSON 代码，并将文件保存为“metadata.json”。注意将第三行image那里改成刚才的链接。</p><pre data-type="codeBlock" text="{ 
  &quot;description&quot;: &quot;This NFT proves I&apos;ve created and deployed my first ERC20 smart contract on Rinkeby with Alchemy Road to Web3&quot;,
  &quot;external_url&quot;: &quot;Alchemy.com&quot;,
  &quot;image&quot;: &quot;https://ipfs.filebase.io/ipfs/bafybeihyvhgbcov2nmvbnveunoodokme5eb42uekrqowxdennt2qyeculm&quot;,
  &quot;name&quot;: &quot;A cool NFT&quot;, 
  &quot;attributes&quot;: [
    {
      &quot;trait_type&quot;: &quot;Base&quot;, 
      &quot;value&quot;: &quot;Starfish&quot;
    }, 
    {
      &quot;trait_type&quot;: &quot;Eyes&quot;, 
      &quot;value&quot;: &quot;Big&quot;
    }, 
    {
      &quot;trait_type&quot;: &quot;Mouth&quot;, 
      &quot;value&quot;: &quot;Surprised&quot;
    }, 
    {
      &quot;trait_type&quot;: &quot;Level&quot;, 
      &quot;value&quot;: 5
    }, 
    {
      &quot;trait_type&quot;: &quot;Stamina&quot;, 
      &quot;value&quot;: 1.4
    }, 
    {
      &quot;trait_type&quot;: &quot;Personality&quot;, 
      &quot;value&quot;: &quot;Sad&quot;
    }, 
    {
      &quot;display_type&quot;: &quot;boost_number&quot;, 
      &quot;trait_type&quot;: &quot;Aqua Power&quot;, 
      &quot;value&quot;: 40
    }, 
    {
      &quot;display_type&quot;: &quot;boost_percentage&quot;, 
      &quot;trait_type&quot;: &quot;Stamina Increase&quot;, 
      &quot;value&quot;: 10
    }, 
    {
      &quot;display_type&quot;: &quot;number&quot;, 
      &quot;trait_type&quot;: &quot;Generation&quot;, 
      &quot;value&quot;: 2
    }
  }
"><code><span class="hljs-punctuation">{</span> 
  <span class="hljs-attr">"description"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"This NFT proves I've created and deployed my first ERC20 smart contract on Rinkeby with Alchemy Road to Web3"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"external_url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Alchemy.com"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"image"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://ipfs.filebase.io/ipfs/bafybeihyvhgbcov2nmvbnveunoodokme5eb42uekrqowxdennt2qyeculm"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A cool NFT"</span><span class="hljs-punctuation">,</span> 
  <span class="hljs-attr">"attributes"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Base"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Starfish"</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> 
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Eyes"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Big"</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> 
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mouth"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Surprised"</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> 
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Level"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> 
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Stamina"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1.4</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> 
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Personality"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Sad"</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> 
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"display_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"boost_number"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Aqua Power"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">40</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> 
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"display_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"boost_percentage"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Stamina Increase"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">10</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> 
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"display_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"number"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Generation"</span><span class="hljs-punctuation">,</span> 
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span>
    <span class="hljs-punctuation">}</span>
  <span class="hljs-punctuation">}</span>
</code></pre><p>5.回到 Filebase 并上传 <code>metadata.json</code>文件在我们上传图像的同一存储桶中。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a3d50e7af59f568e9ff05130aa659b9371beaef3cb064ac765a2ac32884793fb.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>6.最后，单击 CID 并复制它，在下一部分中需要它来构建 NFT 的令牌 URI：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3f898eb06555ac32f9d043780f906c9a838fb964916d0845a60aa56b64d86c3f.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-step7-mint-your-rinkeby-nft" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step7 Mint Your Rinkeby NFT</h3><p>1.返回 Remix 并在 Deploy &amp; Run Transactions 菜单中，进入“已部署的合约” - 然后单击我们刚刚部署的合约。单击 safeMint 下拉框 <strong>并将您的地址</strong> 和<strong>以下字符串粘贴到 uri</strong> （cid处复制刚才的）字段中。</p><p><code>ipfs://\&lt;your\_metadata\_cid&gt;</code></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/20a507d78bec192594f3fbfd52addccaee8395fbd1372b90321eb330ebc18085.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 费用。</p><p>点击“确认”并继续铸造你的第一个 NFT！</p><p>2.等待几秒钟，为确保铸币成功通过，将您的地址复制并粘贴到 balanceOf 方法输入中点击call，它应该显示您有 1 个 NFT。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/587c1bcd512024e5143ebb3f71dc4c3b2e6d809f62995078ba99f4bcfb71098c.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.在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/cf57d21fa29acb281970fab85f082b9564c3c031c4753ca3c676d84ebfb7c836.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-step8-opensea-nft" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">step8 在 OpenSea 上可视化您的 NFT</h3><p>1.到 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnets.opensea.io/">testnets.opensea.io </a>并 <strong>使用您的 Metamask 钱包登录</strong> 。 然后单击您的个人资料图片，您应该会在那里看到您新铸造的 NFT。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c30a99ceb5c3f8fea086814e353a91ae292dcc1e5ad59a38d38455205bf73fa9.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.如果图像尚不可见，请单击它，然后单击“刷新元数据”按钮。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/35bb65bdcff022b74b2c6d03bbed84dce336dc861f9ccc23a34d6ed216a2bc2e.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.有时 OpenSea 很难识别测试网元数据 - 并且可能需要长达 6 小时才能看到它。 一段 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnets.opensea.io/assets/mumbai/0x5a411430964664412e69cff1134759f6bb57c5d7/1">时间后，您的 NFT 应该如下所示 </a>：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/17f18346d374a76a9e209c9b7a8a84a2dc8b9134c3c8f37ad98e39a57e7ea4ad.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>恭喜，您已成功创建、修改和部署您的第一个智能合约。 铸造了你的第一个 NFT，并在 IPFS 上发布了你的图像！</strong></p>]]></content:encoded>
            <author>sheepswap@newsletter.paragraph.com (Robby)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3第十周教程- 使用 Lens 协议创建去中心化 Twitter]]></title>
            <link>https://paragraph.com/@sheepswap/alchemy-the-road-to-web3-lens-twitter</link>
            <guid>AIqBtpIOCEjyMZ1017Bu</guid>
            <pubDate>Sun, 25 Sep 2022 14:39:09 GMT</pubDate>
            <description><![CDATA[在本课中，将学习：如何使用 Apollo GraphQL 客户端设置 Next.js 应用程序如何使用 Lens 协议 API 获取存储在 Polygon 区块链上的个人资料、帖子和其他数据MintKudos API 简介——以便您可以将您的 PoK 代币集成到您的 dapp 中！Lit 协议简介——如果您想加密某些帖子以仅显示给各个社区成员如何使用 Repl.it 部署你的去中心化社交媒体应用程序前端网站扩展此项目的多个挑战选项！话不多说了，我们开始今天的课程吧。1.设置依赖安装Apollo我们今天的课程需要在VScode中去执行，首先我们需要建立一个项目# 创建一个road-to-lens 这是注释 不要输入命令行 npx create-next-app road-to-lens # 安装graphql npm install @apollo/client graphql # 运行项目验证 npm run dev 当你出现这样的结果，恭喜你已经成功完成第一步了。2.在 index.js 页面上使用 Lens 推荐的配置文件尝试 Apollo GraphQL2.1新建apoll...]]></description>
            <content:encoded><![CDATA[<p>在本课中，将学习：</p><ul><li><p>如何使用 Apollo GraphQL 客户端设置 Next.js 应用程序</p></li><li><p>如何使用 Lens 协议 API 获取存储在 Polygon 区块链上的个人资料、帖子和其他数据</p></li><li><p>MintKudos API 简介——以便您可以将您的 PoK 代币集成到您的 dapp 中！</p></li><li><p>Lit 协议简介——如果您想加密某些帖子以仅显示给各个社区成员</p></li><li><p>如何使用 Repl.it 部署你的去中心化社交媒体应用程序前端网站</p></li><li><p>扩展此项目的多个挑战选项！</p></li></ul><p>话不多说了，我们开始今天的课程吧。</p><h2 id="h-1apollo" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1.设置依赖安装Apollo</h2><p>我们今天的课程需要在VScode中去执行，首先我们需要建立一个项目</p><pre data-type="codeBlock" text="# 创建一个road-to-lens 这是注释 不要输入命令行
npx create-next-app road-to-lens
# 安装graphql
npm install @apollo/client graphql
# 运行项目验证
npm run dev
"><code># 创建一个road<span class="hljs-operator">-</span>to<span class="hljs-operator">-</span>lens 这是注释 不要输入命令行
npx create<span class="hljs-operator">-</span>next<span class="hljs-operator">-</span>app road<span class="hljs-operator">-</span>to<span class="hljs-operator">-</span>lens
# 安装graphql
npm install @apollo<span class="hljs-operator">/</span>client graphql
# 运行项目验证
npm run dev
</code></pre><p>当你出现这样的结果，恭喜你已经成功完成第一步了。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f2e6d03237bf7af023c18b697aeeaed092c0c70c5e5e40acc5b94007e508ee6e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-2-indexjs-lens-apollo-graphql" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.在 index.js 页面上使用 Lens 推荐的配置文件尝试 Apollo GraphQL</h2><h3 id="h-21apollo-clientjs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.1新建apollo-client.js</h3><p>我们在当前项目新建一个apollo-client.js输入下面的内容：</p><pre data-type="codeBlock" text="// ./apollo-client.js

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  fragment MediaFields on Media {
    url
    mimeType
  }

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

  fragment PublicationStatsFields on PublicationStats {
    totalAmountOfMirrors
    totalAmountOfCollects
    totalAmountOfComments
  }

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

  fragment Erc20Fields on Erc20 {
    name
    symbol
    decimals
    address
  }

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

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

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

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

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

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

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

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

  fragment MediaFields on Media {
    url
    mimeType
  }

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

  fragment PublicationStatsFields on PublicationStats {
    totalAmountOfMirrors
    totalAmountOfCollects
    totalAmountOfComments
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

contract NFTMarketplace is ERC721URIStorage {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return newTokenId;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC721</span>(<span class="hljs-params"><span class="hljs-string">"NFTMarketplace"</span>, <span class="hljs-string">"NFTM"</span></span>) </span>{
        owner <span class="hljs-operator">=</span> <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
    }

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

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getListPrice</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> listPrice;
    }

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

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

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCurrentToken</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> _tokenIds.current();
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    });
};

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

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

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

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

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

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

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

    });
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    updateFetched(true);
    updateData(items);
}

if(!dataFetched)
    getAllNFTs();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

export default function NFTPage(props) {

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

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

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

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

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

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

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

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

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

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

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

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

            <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"text-green text-center mt-3"</span><span class="hljs-operator">></span>{message}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  )
}
</code></pre><h2 id="h-7" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">7.测试代码</h2><p>我们在shell输入npm start 则会出现这样的页面</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/511bb5e996725cb31ae5ed1df61d12b7e34cfb985ac6570e0f59eafa588f7d81.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5b0227b3d7fb414d6cb8a7c43c9251d43d2cd055ab06e6899cc186eebb83fdfc.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>sheepswap@newsletter.paragraph.com (Robby)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3第六周文本教程- 构建 Staking Dapp]]></title>
            <link>https://paragraph.com/@sheepswap/alchemy-the-road-to-web3-staking-dapp</link>
            <guid>PTpDula9s7aV5WuOzhO4</guid>
            <pubDate>Sun, 25 Sep 2022 14:25:24 GMT</pubDate>
            <description><![CDATA[Gitpod是一个基于Chorom Cloud平台的在线IDE，它可以快速的启动一个基于大多数流行语言的开发环境，并且可以很顺畅的进行开发。是一款在线IDE能即时修改github代码。如何使用Gitpod在chrome浏览器中输入： https://gitpod.io/#github.com/banq/jdonframework 注意，github.com/banq/jdonframework代表你要参与修改的github项目。当键入以后，会提示你授权github账户给它，然后提示你安装chrome插件，这一切安装好的，一个IDE界面显示出来，里面的项目就是你要参与的项目源码，你可以更改代码，然后提交git，也可以进行pull request. 默认git push 是不上去的，它会提醒并跳到配置界面，我们需要，开放配置公开仓库的写权限。如下所示：我们已经学会了如何从头开始使用 Hardhat，构建我们自己的前端，甚至编写 Solidity。 虽然所有这些技能对于希望建立坚实基础的开发人员都非常有价值，但也有一些工具可以帮助抽象环境设置和依赖项的一些复杂性，从而使开发人员能够更轻...]]></description>
            <content:encoded><![CDATA[<p>Gitpod是一个基于Chorom Cloud平台的在线IDE，它可以快速的启动一个基于大多数流行语言的开发环境，并且可以很顺畅的进行开发。是一款在线IDE能即时修改github代码。</p><h3 id="h-gitpod" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">如何使用Gitpod</h3><p>在chrome浏览器中输入： <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://gitpod.io/#github.com/banq/jdonframework">https://gitpod.io/#github.com/banq/jdonframework</a></p><p>注意，<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://github.com/banq/jdonframework%E4%BB%A3%E8%A1%A8%E4%BD%A0%E8%A6%81%E5%8F%82%E4%B8%8E%E4%BF%AE%E6%94%B9%E7%9A%84github%E9%A1%B9%E7%9B%AE%E3%80%82">github.com/banq/jdonframework代表你要参与修改的github项目。</a>当键入以后，会提示你授权github账户给它，然后提示你安装chrome插件，这一切安装好的，一个IDE界面显示出来，里面的项目就是你要参与的项目源码，你可以更改代码，然后提交git，也可以进行pull request. 默认git push 是不上去的，它会提醒并跳到配置界面，我们需要，开放配置公开仓库的写权限。如下所示：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/66fcfbf4fa1a88c68a9dc8871d24d54df74b4d36b40471925d7d9c9196912143.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>我们已经学会了如何从头开始使用 Hardhat，构建我们自己的前端，甚至编写 Solidity。</p><p>虽然所有这些技能对于希望建立坚实基础的开发人员都非常有价值，但也有一些工具可以帮助抽象环境设置和依赖项的一些复杂性，从而使开发人员能够更轻松地进行修补！</p><p>我们推荐的这些工具之一是<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://scaffoldeth.io/">脚手架-eth</a>！</p><p>Scaffold-eth 的核心是为以太坊上的快速原型设计提供现成的堆栈，使开发人员能够访问最先进的工具来快速学习/发布基于以太坊的 dApp。使用 Scaffold-eth 和 Alchemy，可以轻松地在区块链上合成和部署代码。如果不熟悉加密质押，最好将其概括为将加密资产锁定/存入 DeFi 协议或智能合约以赚取利息的过程。Staking 加密已成为许多 DeFi 协议的基石，并允许开发人员创建复杂的金融衍生产品。虽然大多数 DeFi 质押合约都非常复杂，但我们将研究最基本的合约之一，以便我们学习关键概念。</p><p>我们将一起学习以下赌注的构建块：</p><ul><li><p>使用 Scaffold-Eth 构建</p><ul><li><p>一起破解前端</p></li><li><p>打造 Solidity “后端”</p></li></ul></li><li><p>将 ETH 从钱包转移到智能合约，反之亦然</p></li><li><p>使用 Solidity 修饰符</p></li></ul><h2 id="h-1-scaffold-eth" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1. 下载 Scaffold-Eth</h2><p>在浏览器输入： <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://gitpod.io/#https://github.com/scaffold-eth/scaffold-eth-challenges/tree/challenge-1-decentralized-staking">https://gitpod.io/#github.com/scaffold-eth/scaffold-eth-challenges/tree/challenge-1-decentralized-staking</a>，当我们进入的时候会提示我们登录，我们这里可以选择使用Github登录，没有的同学去申请注册一个。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0ac21caf70ecd5668729ed3f6d1ec5aa283ce5fdad6bdf37edb0c296856fe893.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>选择我们需要使用的工具基于浏览器的VScode，等待我们项目初始化完成。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e0518e94ddabf66fd5dbc03c7d786d5d563020cfcece964f9010a93a438dbe4c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>当出现下面的页面则说明我们的项目初始化可以了。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/de1cf428e2d09248ddcff454f9e8e1bab707eab870a97a7dba1e19d84631cb1b.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>因为我们后续需要去提交Git的地址，所以这里需要去新建一个分支：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e4e4e9f3fea9829c1bd8f19913c1f0c77067f40ec1b06c37817e8b30481f566b.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/e649a44dbaffb110628be6a97ec4010ff61da93d19cd4fc9ad57ee74239f406a.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>当我们点击publish branch的时候，页面右下角会提示我们如下所示，第一个问题，我们上面说了默认权限未开启，需要我们去开启，按照上面的开启即可，因为我们是clone人家的代码，作者是没有给你权限进行修改的，所以我们需要去fork作者的代码在自己的仓库，如下操作即可。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c8e01ee8f72afd22d3734728df49ebeba9375b8a04bf9201d6d75f180da7c81c.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>当我们把代码fork到我们仓库之后下，下面就该修改我们的代码了，我们来到代码的地方进行修改。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/508a1cebdf8d5674177468d2649b594344c60246aaaa17f222721f4b3f1d7154.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>代码如下：</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import &quot;hardhat/console.sol&quot;;
import &quot;./ExampleExternalContract.sol&quot;;

contract Staker {

  ExampleExternalContract public exampleExternalContract;

  mapping(address =&gt; uint256) public balances;
  mapping(address =&gt; uint256) public depositTimestamps;

  uint256 public constant rewardRatePerSecond = 0.1 ether;
  uint256 public withdrawalDeadline = block.timestamp + 120 seconds;
  uint256 public claimDeadline = block.timestamp + 240 seconds;
  uint256 public currentBlock = 0;

  // Events
  event Stake(address indexed sender, uint256 amount);
  event Received(address, uint);
  event Execute(address indexed sender, uint256 amount);

  // Modifiers
  /*
  Checks if the withdrawal period has been reached or not
  */
  modifier withdrawalDeadlineReached( bool requireReached ) {
    uint256 timeRemaining = withdrawalTimeLeft();
    if( requireReached ) {
      require(timeRemaining == 0, &quot;Withdrawal period is not reached yet&quot;);
    } else {
      require(timeRemaining &gt; 0, &quot;Withdrawal period has been reached&quot;);
    }
    _;
  }

  /*
  Checks if the claim period has ended or not
  */
  modifier claimDeadlineReached( bool requireReached ) {
    uint256 timeRemaining = claimPeriodLeft();
    if( requireReached ) {
      require(timeRemaining == 0, &quot;Claim deadline is not reached yet&quot;);
    } else {
      require(timeRemaining &gt; 0, &quot;Claim deadline has been reached&quot;);
    }
    _;
  }

  /*
  Requires that the contract only be completed once!
  */
  modifier notCompleted() {
    bool completed = exampleExternalContract.completed();
    require(!completed, &quot;Stake already completed!&quot;);
    _;
  }

  constructor(address exampleExternalContractAddress){
      exampleExternalContract = ExampleExternalContract(exampleExternalContractAddress);
  }

  // Stake function for a user to stake ETH in our contract
  function stake() public payable withdrawalDeadlineReached(false) claimDeadlineReached(false){
    balances[msg.sender] = balances[msg.sender] + msg.value;
    depositTimestamps[msg.sender] = block.timestamp;
    emit Stake(msg.sender, msg.value);
  }

  /*
  Withdraw function for a user to remove their staked ETH inclusive
  of both principal and any accrued interest
  */
  function withdraw() public withdrawalDeadlineReached(true) claimDeadlineReached(false) notCompleted{
    require(balances[msg.sender] &gt; 0, &quot;You have no balance to withdraw!&quot;);
    uint256 individualBalance = balances[msg.sender];
    uint256 indBalanceRewards = individualBalance + ((block.timestamp-depositTimestamps[msg.sender])*rewardRatePerSecond);
    balances[msg.sender] = 0;

    // Transfer all ETH via call! (not transfer) cc: https://solidity-by-example.org/sending-ether
    (bool sent, bytes memory data) = msg.sender.call{value: indBalanceRewards}(&quot;&quot;);
    require(sent, &quot;RIP; withdrawal failed :( &quot;);
  }

  /*
  Allows any user to repatriate &quot;unproductive&quot; funds that are left in the staking contract
  past the defined withdrawal period
  */
  function execute() public claimDeadlineReached(true) notCompleted {
    uint256 contractBalance = address(this).balance;
    exampleExternalContract.complete{value: address(this).balance}();
  }

  /*
  READ-ONLY function to calculate the time remaining before the minimum staking period has passed
  */
  function withdrawalTimeLeft() public view returns (uint256 withdrawalTimeLeft) {
    if( block.timestamp &gt;= withdrawalDeadline) {
      return (0);
    } else {
      return (withdrawalDeadline - block.timestamp);
    }
  }

  /*
  READ-ONLY function to calculate the time remaining before the minimum staking period has passed
  */
  function claimPeriodLeft() public view returns (uint256 claimPeriodLeft) {
    if( block.timestamp &gt;= claimDeadline) {
      return (0);
    } else {
      return (claimDeadline - block.timestamp);
    }
  }

  /*
  Time to &quot;kill-time&quot; on our local testnet
  */
  function killTime() public {
    currentBlock = block.timestamp;
  }

  /*
  \Function for our smart contract to receive ETH
  cc: https://docs.soliditylang.org/en/latest/contracts.html#receive-ether-function
  */
  receive() external payable {
      emit Received(msg.sender, msg.value);
  }
}
"><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.4;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"hardhat/console.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./ExampleExternalContract.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Staker</span> </span>{

  ExampleExternalContract <span class="hljs-keyword">public</span> exampleExternalContract;

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

  <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> rewardRatePerSecond <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>;
  <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> withdrawalDeadline <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">+</span> <span class="hljs-number">120</span> <span class="hljs-literal">seconds</span>;
  <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> claimDeadline <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">+</span> <span class="hljs-number">240</span> <span class="hljs-literal">seconds</span>;
  <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> currentBlock <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;

  <span class="hljs-comment">// Events</span>
  <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Stake</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> sender, <span class="hljs-keyword">uint256</span> amount</span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Received</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>, <span class="hljs-keyword">uint</span></span>)</span>;
  <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Execute</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> sender, <span class="hljs-keyword">uint256</span> amount</span>)</span>;

  <span class="hljs-comment">// Modifiers</span>
  <span class="hljs-comment">/*
  Checks if the withdrawal period has been reached or not
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">withdrawalDeadlineReached</span>(<span class="hljs-params"> <span class="hljs-keyword">bool</span> requireReached </span>) </span>{
    <span class="hljs-keyword">uint256</span> timeRemaining <span class="hljs-operator">=</span> withdrawalTimeLeft();
    <span class="hljs-keyword">if</span>( requireReached ) {
      <span class="hljs-built_in">require</span>(timeRemaining <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"Withdrawal period is not reached yet"</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-built_in">require</span>(timeRemaining <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Withdrawal period has been reached"</span>);
    }
    <span class="hljs-keyword">_</span>;
  }

  <span class="hljs-comment">/*
  Checks if the claim period has ended or not
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">claimDeadlineReached</span>(<span class="hljs-params"> <span class="hljs-keyword">bool</span> requireReached </span>) </span>{
    <span class="hljs-keyword">uint256</span> timeRemaining <span class="hljs-operator">=</span> claimPeriodLeft();
    <span class="hljs-keyword">if</span>( requireReached ) {
      <span class="hljs-built_in">require</span>(timeRemaining <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"Claim deadline is not reached yet"</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-built_in">require</span>(timeRemaining <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Claim deadline has been reached"</span>);
    }
    <span class="hljs-keyword">_</span>;
  }

  <span class="hljs-comment">/*
  Requires that the contract only be completed once!
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">notCompleted</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">bool</span> completed <span class="hljs-operator">=</span> exampleExternalContract.completed();
    <span class="hljs-built_in">require</span>(<span class="hljs-operator">!</span>completed, <span class="hljs-string">"Stake already completed!"</span>);
    <span class="hljs-keyword">_</span>;
  }

  <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> exampleExternalContractAddress</span>)</span>{
      exampleExternalContract <span class="hljs-operator">=</span> ExampleExternalContract(exampleExternalContractAddress);
  }

  <span class="hljs-comment">// Stake function for a user to stake ETH in our contract</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">stake</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">payable</span></span> <span class="hljs-title">withdrawalDeadlineReached</span>(<span class="hljs-params"><span class="hljs-literal">false</span></span>) <span class="hljs-title">claimDeadlineReached</span>(<span class="hljs-params"><span class="hljs-literal">false</span></span>)</span>{
    balances[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> balances[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">+</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>;
    depositTimestamps[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>;
    <span class="hljs-keyword">emit</span> Stake(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>);
  }

  <span class="hljs-comment">/*
  Withdraw function for a user to remove their staked ETH inclusive
  of both principal and any accrued interest
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">withdrawalDeadlineReached</span>(<span class="hljs-params"><span class="hljs-literal">true</span></span>) <span class="hljs-title">claimDeadlineReached</span>(<span class="hljs-params"><span class="hljs-literal">false</span></span>) <span class="hljs-title">notCompleted</span></span>{
    <span class="hljs-built_in">require</span>(balances[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"You have no balance to withdraw!"</span>);
    <span class="hljs-keyword">uint256</span> individualBalance <span class="hljs-operator">=</span> balances[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>];
    <span class="hljs-keyword">uint256</span> indBalanceRewards <span class="hljs-operator">=</span> individualBalance <span class="hljs-operator">+</span> ((<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>-depositTimestamps[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>])<span class="hljs-operator">*</span>rewardRatePerSecond);
    balances[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;

    <span class="hljs-comment">// Transfer all ETH via call! (not transfer) cc: https://solidity-by-example.org/sending-ether</span>
    (<span class="hljs-keyword">bool</span> sent, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data) <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>.<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: indBalanceRewards}(<span class="hljs-string">""</span>);
    <span class="hljs-built_in">require</span>(sent, <span class="hljs-string">"RIP; withdrawal failed :( "</span>);
  }

  <span class="hljs-comment">/*
  Allows any user to repatriate "unproductive" funds that are left in the staking contract
  past the defined withdrawal period
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">execute</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">claimDeadlineReached</span>(<span class="hljs-params"><span class="hljs-literal">true</span></span>) <span class="hljs-title">notCompleted</span> </span>{
    <span class="hljs-keyword">uint256</span> contractBalance <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>;
    exampleExternalContract.complete{<span class="hljs-built_in">value</span>: <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>}();
  }

  <span class="hljs-comment">/*
  READ-ONLY function to calculate the time remaining before the minimum staking period has passed
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdrawalTimeLeft</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> withdrawalTimeLeft</span>) </span>{
    <span class="hljs-keyword">if</span>( <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">></span><span class="hljs-operator">=</span> withdrawalDeadline) {
      <span class="hljs-keyword">return</span> (<span class="hljs-number">0</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> (withdrawalDeadline <span class="hljs-operator">-</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>);
    }
  }

  <span class="hljs-comment">/*
  READ-ONLY function to calculate the time remaining before the minimum staking period has passed
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">claimPeriodLeft</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> claimPeriodLeft</span>) </span>{
    <span class="hljs-keyword">if</span>( <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">></span><span class="hljs-operator">=</span> claimDeadline) {
      <span class="hljs-keyword">return</span> (<span class="hljs-number">0</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> (claimDeadline <span class="hljs-operator">-</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>);
    }
  }

  <span class="hljs-comment">/*
  Time to "kill-time" on our local testnet
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">killTime</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
    currentBlock <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>;
  }

  <span class="hljs-comment">/*
  \Function for our smart contract to receive ETH
  cc: https://docs.soliditylang.org/en/latest/contracts.html#receive-ether-function
  */</span>
  <span class="hljs-function"><span class="hljs-keyword">receive</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
      <span class="hljs-keyword">emit</span> Received(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</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/e8b34f1380276d7ab361a5971c9f0f3945e7ceb4ffb0fdb2c162b77e1cfd2d1d.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 WalletConnectProvider from &quot;@walletconnect/web3-provider&quot;;
//import Torus from &quot;@toruslabs/torus-embed&quot;
import WalletLink from &quot;walletlink&quot;;
import { Alert, Button, Col, Menu, Row, List, Divider } from &quot;antd&quot;;
import &quot;antd/dist/antd.css&quot;;
import React, { useCallback, useEffect, useState } from &quot;react&quot;;
import { BrowserRouter, Link, Route, Switch } from &quot;react-router-dom&quot;;
import Web3Modal from &quot;web3modal&quot;;
import &quot;./App.css&quot;;
import { Account, Address, Balance, Contract, Faucet, GasGauge, Header, Ramp, ThemeSwitch } from &quot;./components&quot;;
import { INFURA_ID, NETWORK, NETWORKS } from &quot;./constants&quot;;
import { Transactor } from &quot;./helpers&quot;;
import {
  useBalance,
  useContractLoader,
  useContractReader,
  useGasPrice,
  useOnBlock,
  useUserProviderAndSigner,
} from &quot;eth-hooks&quot;;
import { useEventListener } from &quot;eth-hooks/events/useEventListener&quot;;
import { useExchangeEthPrice } from &quot;eth-hooks/dapps/dex&quot;;
// import Hints from &quot;./Hints&quot;;
import { ExampleUI, Hints, Subgraph } from &quot;./views&quot;;

import { useContractConfig } from &quot;./hooks&quot;;
import Portis from &quot;@portis/web3&quot;;
import Fortmatic from &quot;fortmatic&quot;;
import Authereum from &quot;authereum&quot;;
import humanizeDuration from &quot;humanize-duration&quot;;

const { ethers } = require(&quot;ethers&quot;);
/*
    Welcome to 🏗 scaffold-eth !

    Code:
    https://github.com/austintgriffith/scaffold-eth

    Support:
    https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA
    or DM @austingriffith on Twitter or Telegram

    You should get your own Infura.io ID and put it in `constants.js`
    (this is your connection to the main Ethereum network for ENS etc.)


    🌏 EXTERNAL CONTRACTS:
    You can also bring in contract artifacts in `constants.js`
    (and then use the `useExternalContractLoader()` hook!)
*/

/// 📡 What chain are your contracts deployed to?
const targetNetwork = NETWORKS.localhost; // &lt;------- select your target frontend network (localhost, rinkeby, xdai, mainnet)

// 😬 Sorry for all the console logging
const DEBUG = true;
const NETWORKCHECK = true;

// 🛰 providers
if (DEBUG) console.log(&quot;📡 Connecting to Mainnet Ethereum&quot;);
// const mainnetProvider = getDefaultProvider(&quot;mainnet&quot;, { infura: INFURA_ID, etherscan: ETHERSCAN_KEY, quorum: 1 });
// const mainnetProvider = new InfuraProvider(&quot;mainnet&quot;,INFURA_ID);
//
// attempt to connect to our own scaffold eth rpc and if that fails fall back to infura...
// Using StaticJsonRpcProvider as the chainId won&apos;t change see https://github.com/ethers-io/ethers.js/issues/901
const scaffoldEthProvider = navigator.onLine
  ? new ethers.providers.StaticJsonRpcProvider(&quot;https://rpc.scaffoldeth.io:48544&quot;)
  : null;
const poktMainnetProvider = navigator.onLine
  ? new ethers.providers.StaticJsonRpcProvider(
      &quot;https://eth-mainnet.gateway.pokt.network/v1/lb/611156b4a585a20035148406&quot;,
    )
  : null;
const mainnetInfura = navigator.onLine
  ? new ethers.providers.StaticJsonRpcProvider(&quot;https://mainnet.infura.io/v3/&quot; + INFURA_ID)
  : null;
// ( ⚠️ Getting &quot;failed to meet quorum&quot; errors? Check your INFURA_ID

// 🏠 Your local provider is usually pointed at your local blockchain
const localProviderUrl = targetNetwork.rpcUrl;
// as you deploy to other networks you can set REACT_APP_PROVIDER=https://dai.poa.network in packages/react-app/.env
const localProviderUrlFromEnv = process.env.REACT_APP_PROVIDER ? process.env.REACT_APP_PROVIDER : localProviderUrl;
if (DEBUG) console.log(&quot;🏠 Connecting to provider:&quot;, localProviderUrlFromEnv);
const localProvider = new ethers.providers.StaticJsonRpcProvider(localProviderUrlFromEnv);

// 🔭 block explorer URL
const blockExplorer = targetNetwork.blockExplorer;

// Coinbase walletLink init
const walletLink = new WalletLink({
  appName: &quot;coinbase&quot;,
});

// WalletLink provider
const walletLinkProvider = walletLink.makeWeb3Provider(`https://mainnet.infura.io/v3/${INFURA_ID}`, 1);

// Portis ID: 6255fb2b-58c8-433b-a2c9-62098c05ddc9
/*
  Web3 modal helps us &quot;connect&quot; external wallets:
*/
const web3Modal = new Web3Modal({
  network: &quot;mainnet&quot;, // Optional. If using WalletConnect on xDai, change network to &quot;xdai&quot; and add RPC info below for xDai chain.
  cacheProvider: true, // optional
  theme: &quot;light&quot;, // optional. Change to &quot;dark&quot; for a dark theme.
  providerOptions: {
    walletconnect: {
      package: WalletConnectProvider, // required
      options: {
        bridge: &quot;https://polygon.bridge.walletconnect.org&quot;,
        infuraId: INFURA_ID,
        rpc: {
          1: `https://mainnet.infura.io/v3/${INFURA_ID}`, // mainnet // For more WalletConnect providers: https://docs.walletconnect.org/quick-start/dapps/web3-provider#required
          42: `https://kovan.infura.io/v3/${INFURA_ID}`,
          100: &quot;https://dai.poa.network&quot;, // xDai
        },
      },
    },
    portis: {
      display: {
        logo: &quot;https://user-images.githubusercontent.com/9419140/128913641-d025bc0c-e059-42de-a57b-422f196867ce.png&quot;,
        name: &quot;Portis&quot;,
        description: &quot;Connect to Portis App&quot;,
      },
      package: Portis,
      options: {
        id: &quot;6255fb2b-58c8-433b-a2c9-62098c05ddc9&quot;,
      },
    },
    fortmatic: {
      package: Fortmatic, // required
      options: {
        key: &quot;pk_live_5A7C91B2FC585A17&quot;, // required
      },
    },
    // torus: {
    //   package: Torus,
    //   options: {
    //     networkParams: {
    //       host: &quot;https://localhost:8545&quot;, // optional
    //       chainId: 1337, // optional
    //       networkId: 1337 // optional
    //     },
    //     config: {
    //       buildEnv: &quot;development&quot; // optional
    //     },
    //   },
    // },
    &quot;custom-walletlink&quot;: {
      display: {
        logo: &quot;https://play-lh.googleusercontent.com/PjoJoG27miSglVBXoXrxBSLveV6e3EeBPpNY55aiUUBM9Q1RCETKCOqdOkX2ZydqVf0&quot;,
        name: &quot;Coinbase&quot;,
        description: &quot;Connect to Coinbase Wallet (not Coinbase App)&quot;,
      },
      package: walletLinkProvider,
      connector: async (provider, _options) =&gt; {
        await provider.enable();
        return provider;
      },
    },
    authereum: {
      package: Authereum, // required
    },
  },
});

function App(props) {
  const mainnetProvider =
    poktMainnetProvider &amp;&amp; poktMainnetProvider._isProvider
      ? poktMainnetProvider
      : scaffoldEthProvider &amp;&amp; scaffoldEthProvider._network
      ? scaffoldEthProvider
      : mainnetInfura;

  const [injectedProvider, setInjectedProvider] = useState();
  const [address, setAddress] = useState();

  const logoutOfWeb3Modal = async () =&gt; {
    await web3Modal.clearCachedProvider();
    if (injectedProvider &amp;&amp; injectedProvider.provider &amp;&amp; typeof injectedProvider.provider.disconnect == &quot;function&quot;) {
      await injectedProvider.provider.disconnect();
    }
    setTimeout(() =&gt; {
      window.location.reload();
    }, 1);
  };

  /* 💵 This hook will get the price of ETH from 🦄 Uniswap: */
  const price = useExchangeEthPrice(targetNetwork, mainnetProvider);

  /* 🔥 This hook will get the price of Gas from ⛽️ EtherGasStation */
  const gasPrice = useGasPrice(targetNetwork, &quot;fast&quot;);
  // Use your injected provider from 🦊 Metamask or if you don&apos;t have it then instantly generate a 🔥 burner wallet.
  const userProviderAndSigner = useUserProviderAndSigner(injectedProvider, localProvider);
  const userSigner = userProviderAndSigner.signer;

  useEffect(() =&gt; {
    async function getAddress() {
      if (userSigner) {
        const newAddress = await userSigner.getAddress();
        setAddress(newAddress);
      }
    }
    getAddress();
  }, [userSigner]);

  // You can warn the user if you would like them to be on a specific network
  const localChainId = localProvider &amp;&amp; localProvider._network &amp;&amp; localProvider._network.chainId;
  const selectedChainId =
    userSigner &amp;&amp; userSigner.provider &amp;&amp; userSigner.provider._network &amp;&amp; userSigner.provider._network.chainId;

  // For more hooks, check out 🔗eth-hooks at: https://www.npmjs.com/package/eth-hooks

  // The transactor wraps transactions and provides notificiations
  const tx = Transactor(userSigner, gasPrice);

  // Faucet Tx can be used to send funds from the faucet
  const faucetTx = Transactor(localProvider, gasPrice);

  // 🏗 scaffold-eth is full of handy hooks like this one to get your balance:
  const yourLocalBalance = useBalance(localProvider, address);

  // Just plug in different 🛰 providers to get your balance on different chains:
  const yourMainnetBalance = useBalance(mainnetProvider, address);

  const contractConfig = useContractConfig();

  // Load in your local 📝 contract and read a value from it:
  const readContracts = useContractLoader(localProvider, contractConfig);

  // If you want to make 🔐 write transactions to your contracts, use the userSigner:
  const writeContracts = useContractLoader(userSigner, contractConfig, localChainId);

  // EXTERNAL CONTRACT EXAMPLE:
  //
  // If you want to bring in the mainnet DAI contract it would look like:
  const mainnetContracts = useContractLoader(mainnetProvider, contractConfig);

  // If you want to call a function on a new block
  useOnBlock(mainnetProvider, () =&gt; {
    console.log(`⛓ A new mainnet block is here: ${mainnetProvider._lastBlockNumber}`);
  });

  // Then read your DAI balance like:
  const myMainnetDAIBalance = useContractReader(mainnetContracts, &quot;DAI&quot;, &quot;balanceOf&quot;, [
    &quot;0x34aA3F359A9D614239015126635CE7732c18fDF3&quot;,
  ]);

  //keep track of contract balance to know how much has been staked total:
  const stakerContractBalance = useBalance(
    localProvider,
    readContracts &amp;&amp; readContracts.Staker ? readContracts.Staker.address : null,
  );
  if (DEBUG) console.log(&quot;💵 stakerContractBalance&quot;, stakerContractBalance);

  const rewardRatePerSecond = useContractReader(readContracts, &quot;Staker&quot;, &quot;rewardRatePerSecond&quot;);
  console.log(&quot;💵 Reward Rate:&quot;, rewardRatePerSecond);

  // ** keep track of a variable from the contract in the local React state:
  const balanceStaked = useContractReader(readContracts, &quot;Staker&quot;, &quot;balances&quot;, [address]);
  console.log(&quot;💸 balanceStaked:&quot;, balanceStaked);

  // ** 📟 Listen for broadcast events
  const stakeEvents = useEventListener(readContracts, &quot;Staker&quot;, &quot;Stake&quot;, localProvider, 1);
  console.log(&quot;📟 stake events:&quot;, stakeEvents);

  const receiveEvents = useEventListener(readContracts, &quot;Staker&quot;, &quot;Received&quot;, localProvider, 1);
  console.log(&quot;📟 receive events:&quot;, receiveEvents);

  // ** keep track of a variable from the contract in the local React state:
  const claimPeriodLeft = useContractReader(readContracts, &quot;Staker&quot;, &quot;claimPeriodLeft&quot;);
  console.log(&quot;⏳ Claim Period Left:&quot;, claimPeriodLeft);

  const withdrawalTimeLeft = useContractReader(readContracts, &quot;Staker&quot;, &quot;withdrawalTimeLeft&quot;);
  console.log(&quot;⏳ Withdrawal Time Left:&quot;, withdrawalTimeLeft);


  // ** Listen for when the contract has been &apos;completed&apos;
  const complete = useContractReader(readContracts, &quot;ExampleExternalContract&quot;, &quot;completed&quot;);
  console.log(&quot;✅ complete:&quot;, complete);

  const exampleExternalContractBalance = useBalance(
    localProvider,
    readContracts &amp;&amp; readContracts.ExampleExternalContract ? readContracts.ExampleExternalContract.address : null,
  );
  if (DEBUG) console.log(&quot;💵 exampleExternalContractBalance&quot;, exampleExternalContractBalance);

  let completeDisplay = &quot;&quot;;
  if (complete) {
    completeDisplay = (
      &lt;div style={{padding: 64, backgroundColor: &quot;#eeffef&quot;, fontWeight: &quot;bold&quot;, color: &quot;rgba(0, 0, 0, 0.85)&quot; }} &gt;
        -- 💀 Staking App Fund Repatriation Executed 🪦 --
        &lt;Balance balance={exampleExternalContractBalance} fontSize={32} /&gt; ETH locked!
      &lt;/div&gt;
    );
  }

  /*
  const addressFromENS = useResolveName(mainnetProvider, &quot;austingriffith.eth&quot;);
  console.log(&quot;🏷 Resolved austingriffith.eth as:&quot;, addressFromENS)
  */

  //
  // 🧫 DEBUG 👨🏻‍🔬
  //
  useEffect(() =&gt; {
    if (
      DEBUG &amp;&amp;
      mainnetProvider &amp;&amp;
      address &amp;&amp;
      selectedChainId &amp;&amp;
      yourLocalBalance &amp;&amp;
      yourMainnetBalance &amp;&amp;
      readContracts &amp;&amp;
      writeContracts &amp;&amp;
      mainnetContracts
    ) {
      console.log(&quot;_____________________________________ 🏗 scaffold-eth _____________________________________&quot;);
      console.log(&quot;🌎 mainnetProvider&quot;, mainnetProvider);
      console.log(&quot;🏠 localChainId&quot;, localChainId);
      console.log(&quot;👩‍💼 selected address:&quot;, address);
      console.log(&quot;🕵🏻‍♂️ selectedChainId:&quot;, selectedChainId);
      console.log(&quot;💵 yourLocalBalance&quot;, yourLocalBalance ? ethers.utils.formatEther(yourLocalBalance) : &quot;...&quot;);
      console.log(&quot;💵 yourMainnetBalance&quot;, yourMainnetBalance ? ethers.utils.formatEther(yourMainnetBalance) : &quot;...&quot;);
      console.log(&quot;📝 readContracts&quot;, readContracts);
      console.log(&quot;🌍 DAI contract on mainnet:&quot;, mainnetContracts);
      console.log(&quot;💵 yourMainnetDAIBalance&quot;, myMainnetDAIBalance);
      console.log(&quot;🔐 writeContracts&quot;, writeContracts);
    }
  }, [
    mainnetProvider,
    address,
    selectedChainId,
    yourLocalBalance,
    yourMainnetBalance,
    readContracts,
    writeContracts,
    mainnetContracts,
  ]);

  let networkDisplay = &quot;&quot;;
  if (NETWORKCHECK &amp;&amp; localChainId &amp;&amp; selectedChainId &amp;&amp; localChainId !== selectedChainId) {
    const networkSelected = NETWORK(selectedChainId);
    const networkLocal = NETWORK(localChainId);
    if (selectedChainId === 1337 &amp;&amp; localChainId === 31337) {
      networkDisplay = (
        &lt;div style={{ zIndex: 2, position: &quot;absolute&quot;, right: 0, top: 60, padding: 16 }}&gt;
          &lt;Alert
            message=&quot;⚠️ Wrong Network ID&quot;
            description={
              &lt;div&gt;
                You have &lt;b&gt;chain id 1337&lt;/b&gt; for localhost and you need to change it to &lt;b&gt;31337&lt;/b&gt; to work with
                HardHat.
                &lt;div&gt;(MetaMask -&amp;gt; Settings -&amp;gt; Networks -&amp;gt; Chain ID -&amp;gt; 31337)&lt;/div&gt;
              &lt;/div&gt;
            }
            type=&quot;error&quot;
            closable={false}
          /&gt;
        &lt;/div&gt;
      );
    } else {
      networkDisplay = (
        &lt;div style={{ zIndex: 2, position: &quot;absolute&quot;, right: 0, top: 60, padding: 16 }}&gt;
          &lt;Alert
            message=&quot;⚠️ Wrong Network&quot;
            description={
              &lt;div&gt;
                You have &lt;b&gt;{networkSelected &amp;&amp; networkSelected.name}&lt;/b&gt; selected and you need to be on{&quot; &quot;}
                &lt;Button
                  onClick={async () =&gt; {
                    const ethereum = window.ethereum;
                    const data = [
                      {
                        chainId: &quot;0x&quot; + targetNetwork.chainId.toString(16),
                        chainName: targetNetwork.name,
                        nativeCurrency: targetNetwork.nativeCurrency,
                        rpcUrls: [targetNetwork.rpcUrl],
                        blockExplorerUrls: [targetNetwork.blockExplorer],
                      },
                    ];
                    console.log(&quot;data&quot;, data);

                    let switchTx;
                    // https://docs.metamask.io/guide/rpc-api.html#other-rpc-methods
                    try {
                      switchTx = await ethereum.request({
                        method: &quot;wallet_switchEthereumChain&quot;,
                        params: [{ chainId: data[0].chainId }],
                      });
                    } catch (switchError) {
                      // not checking specific error code, because maybe we&apos;re not using MetaMask
                      try {
                        switchTx = await ethereum.request({
                          method: &quot;wallet_addEthereumChain&quot;,
                          params: data,
                        });
                      } catch (addError) {
                        // handle &quot;add&quot; error
                      }
                    }

                    if (switchTx) {
                      console.log(switchTx);
                    }
                  }}
                &gt;
                  &lt;b&gt;{networkLocal &amp;&amp; networkLocal.name}&lt;/b&gt;
                &lt;/Button&gt;
              &lt;/div&gt;
            }
            type=&quot;error&quot;
            closable={false}
          /&gt;
        &lt;/div&gt;
      );
    }
  } else {
    networkDisplay = (
      &lt;div style={{ zIndex: -1, position: &quot;absolute&quot;, right: 154, top: 28, padding: 16, color: targetNetwork.color }}&gt;
        {targetNetwork.name}
      &lt;/div&gt;
    );
  }

  const loadWeb3Modal = useCallback(async () =&gt; {
    const provider = await web3Modal.connect();
    setInjectedProvider(new ethers.providers.Web3Provider(provider));

    provider.on(&quot;chainChanged&quot;, chainId =&gt; {
      console.log(`chain changed to ${chainId}! updating providers`);
      setInjectedProvider(new ethers.providers.Web3Provider(provider));
    });

    provider.on(&quot;accountsChanged&quot;, () =&gt; {
      console.log(`account changed!`);
      setInjectedProvider(new ethers.providers.Web3Provider(provider));
    });

    // Subscribe to session disconnection
    provider.on(&quot;disconnect&quot;, (code, reason) =&gt; {
      console.log(code, reason);
      logoutOfWeb3Modal();
    });
  }, [setInjectedProvider]);

  useEffect(() =&gt; {
    if (web3Modal.cachedProvider) {
      loadWeb3Modal();
    }
  }, [loadWeb3Modal]);

  const [route, setRoute] = useState();
  useEffect(() =&gt; {
    setRoute(window.location.pathname);
  }, [setRoute]);

  let faucetHint = &quot;&quot;;
  const faucetAvailable = localProvider &amp;&amp; localProvider.connection &amp;&amp; targetNetwork.name.indexOf(&quot;local&quot;) !== -1;

  const [faucetClicked, setFaucetClicked] = useState(false);
  if (
    !faucetClicked &amp;&amp;
    localProvider &amp;&amp;
    localProvider._network &amp;&amp;
    localProvider._network.chainId === 31337 &amp;&amp;
    yourLocalBalance &amp;&amp;
    ethers.utils.formatEther(yourLocalBalance) &lt;= 0
  ) {
    faucetHint = (
      &lt;div style={{ padding: 16 }}&gt;
        &lt;Button
          type=&quot;primary&quot;
          onClick={() =&gt; {
            faucetTx({
              to: address,
              value: ethers.utils.parseEther(&quot;0.01&quot;),
            });
            setFaucetClicked(true);
          }}
        &gt;
          💰 Grab funds from the faucet ⛽️
        &lt;/Button&gt;
      &lt;/div&gt;
    );
  }

  return (
    &lt;div className=&quot;App&quot;&gt;
      {/* ✏️ Edit the header and change the title to your project name */}
      &lt;Header /&gt;
      {networkDisplay}
      &lt;BrowserRouter&gt;
        &lt;Menu style={{ textAlign: &quot;center&quot; }} selectedKeys={[route]} mode=&quot;horizontal&quot;&gt;
          &lt;Menu.Item key=&quot;/&quot;&gt;
            &lt;Link
              onClick={() =&gt; {
                setRoute(&quot;/&quot;);
              }}
              to=&quot;/&quot;
            &gt;
              Staker UI
            &lt;/Link&gt;
          &lt;/Menu.Item&gt;
          &lt;Menu.Item key=&quot;/contracts&quot;&gt;
            &lt;Link
              onClick={() =&gt; {
                setRoute(&quot;/contracts&quot;);
              }}
              to=&quot;/contracts&quot;
            &gt;
              Debug Contracts
            &lt;/Link&gt;
          &lt;/Menu.Item&gt;
        &lt;/Menu&gt;

        &lt;Switch&gt;
          &lt;Route exact path=&quot;/&quot;&gt;
            {completeDisplay}

            &lt;div style={{ padding: 8, marginTop: 16 }}&gt;
              &lt;div&gt;Staker Contract:&lt;/div&gt;
              &lt;Address value={readContracts &amp;&amp; readContracts.Staker &amp;&amp; readContracts.Staker.address} /&gt;
            &lt;/div&gt;

            &lt;Divider /&gt;

            &lt;div style={{ padding: 8, marginTop: 16 }}&gt;
              &lt;div&gt;Reward Rate Per Second:&lt;/div&gt;
              &lt;Balance balance={rewardRatePerSecond} fontSize={64} /&gt; ETH
            &lt;/div&gt;

            &lt;Divider /&gt;

            &lt;div style={{ padding: 8, marginTop: 16, fontWeight: &quot;bold&quot; }}&gt;
              &lt;div&gt;Claim Period Left:&lt;/div&gt;
              {claimPeriodLeft &amp;&amp; humanizeDuration(claimPeriodLeft.toNumber() * 1000)}
            &lt;/div&gt;

            &lt;div style={{ padding: 8, marginTop: 16, fontWeight: &quot;bold&quot;}}&gt;
              &lt;div&gt;Withdrawal Period Left:&lt;/div&gt;
              {withdrawalTimeLeft &amp;&amp; humanizeDuration(withdrawalTimeLeft.toNumber() * 1000)}
            &lt;/div&gt;

            &lt;Divider /&gt;

            &lt;div style={{ padding: 8, fontWeight: &quot;bold&quot;}}&gt;
              &lt;div&gt;Total Available ETH in Contract:&lt;/div&gt;
              &lt;Balance balance={stakerContractBalance} fontSize={64} /&gt;
            &lt;/div&gt;

            &lt;Divider /&gt;

            &lt;div style={{ padding: 8,fontWeight: &quot;bold&quot; }}&gt;
              &lt;div&gt;ETH Locked 🔒 in Staker Contract:&lt;/div&gt;
              &lt;Balance balance={balanceStaked} fontSize={64} /&gt;
            &lt;/div&gt;

            &lt;div style={{ padding: 8 }}&gt;
              &lt;Button
                type={&quot;default&quot;}
                onClick={() =&gt; {
                  tx(writeContracts.Staker.execute());
                }}
              &gt;
                📡 Execute!
              &lt;/Button&gt;
            &lt;/div&gt;

            &lt;div style={{ padding: 8 }}&gt;
              &lt;Button
                type={&quot;default&quot;}
                onClick={() =&gt; {
                  tx(writeContracts.Staker.withdraw());
                }}
              &gt;
                🏧 Withdraw
              &lt;/Button&gt;
            &lt;/div&gt;

            &lt;div style={{ padding: 8 }}&gt;
              &lt;Button
                type={balanceStaked ? &quot;success&quot; : &quot;primary&quot;}
                onClick={() =&gt; {
                  tx(writeContracts.Staker.stake({ value: ethers.utils.parseEther(&quot;0.5&quot;) }));
                }}
              &gt;
                🥩 Stake 0.5 ether!
              &lt;/Button&gt;
            &lt;/div&gt;

            {/*
                🎛 this scaffolding is full of commonly used components
                this &lt;Contract/&gt; component will automatically parse your ABI
                and give you a form to interact with it locally
            */}

            {/* uncomment for a second contract:
            &lt;Contract
              name=&quot;SecondContract&quot;
              signer={userProvider.getSigner()}
              provider={localProvider}
              address={address}
              blockExplorer={blockExplorer}
              contractConfig={contractConfig}
            /&gt;
            */}
          &lt;/Route&gt;
          &lt;Route path=&quot;/contracts&quot;&gt;
            &lt;Contract
              name=&quot;Staker&quot;
              signer={userSigner}
              provider={localProvider}
              address={address}
              blockExplorer={blockExplorer}
              contractConfig={contractConfig}
            /&gt;
            &lt;Contract
              name=&quot;ExampleExternalContract&quot;
              signer={userSigner}
              provider={localProvider}
              address={address}
              blockExplorer={blockExplorer}
              contractConfig={contractConfig}
            /&gt;
          &lt;/Route&gt;
        &lt;/Switch&gt;
      &lt;/BrowserRouter&gt;

      &lt;ThemeSwitch /&gt;

      {/* 👨‍💼 Your account is in the top right with a wallet at connect options */}
      &lt;div style={{ position: &quot;fixed&quot;, textAlign: &quot;right&quot;, right: 0, top: 0, padding: 10 }}&gt;
        &lt;Account
          address={address}
          localProvider={localProvider}
          userSigner={userSigner}
          mainnetProvider={mainnetProvider}
          price={price}
          web3Modal={web3Modal}
          loadWeb3Modal={loadWeb3Modal}
          logoutOfWeb3Modal={logoutOfWeb3Modal}
          blockExplorer={blockExplorer}
        /&gt;
        {faucetHint}
      &lt;/div&gt;

      &lt;div style={{ marginTop: 32, opacity: 0.5 }}&gt;
        {/* Add your address here */}
        Created by &lt;Address value={&quot;Your...address&quot;} ensProvider={mainnetProvider} fontSize={16} /&gt;
      &lt;/div&gt;

      &lt;div style={{ marginTop: 32, opacity: 0.5 }}&gt;
        &lt;a target=&quot;_blank&quot; style={{ padding: 32, color: &quot;#000&quot; }} href=&quot;https://github.com/scaffold-eth/scaffold-eth&quot;&gt;
          🍴 Fork me!
        &lt;/a&gt;
      &lt;/div&gt;

      {/* 🗺 Extra UI like gas price, eth price, faucet, and support: */}
      &lt;div style={{ position: &quot;fixed&quot;, textAlign: &quot;left&quot;, left: 0, bottom: 20, padding: 10 }}&gt;
        &lt;Row align=&quot;middle&quot; gutter={[4, 4]}&gt;
          &lt;Col span={8}&gt;
            &lt;Ramp price={price} address={address} networks={NETWORKS} /&gt;
          &lt;/Col&gt;

          &lt;Col span={8} style={{ textAlign: &quot;center&quot;, opacity: 0.8 }}&gt;
            &lt;GasGauge gasPrice={gasPrice} /&gt;
          &lt;/Col&gt;
          &lt;Col span={8} style={{ textAlign: &quot;center&quot;, opacity: 1 }}&gt;
            &lt;Button
              onClick={() =&gt; {
                window.open(&quot;https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA&quot;);
              }}
              size=&quot;large&quot;
              shape=&quot;round&quot;
            &gt;
              &lt;span style={{ marginRight: 8 }} role=&quot;img&quot; aria-label=&quot;support&quot;&gt;
                💬
              &lt;/span&gt;
              Support
            &lt;/Button&gt;
          &lt;/Col&gt;
        &lt;/Row&gt;

        &lt;Row align=&quot;middle&quot; gutter={[4, 4]}&gt;
          &lt;Col span={24}&gt;
            {
              /*  if the local provider has a signer, let&apos;s show the faucet:  */
              faucetAvailable ? (
                &lt;Faucet localProvider={localProvider} price={price} ensProvider={mainnetProvider} /&gt;
              ) : (
                &quot;&quot;
              )
            }
          &lt;/Col&gt;
        &lt;/Row&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default App;
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">WalletConnectProvider</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@walletconnect/web3-provider"</span>;
<span class="hljs-comment">//import Torus from "@toruslabs/torus-embed"</span>
<span class="hljs-keyword">import</span> <span class="hljs-title">WalletLink</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"walletlink"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Alert</span>, <span class="hljs-title">Button</span>, <span class="hljs-title">Col</span>, <span class="hljs-title">Menu</span>, <span class="hljs-title">Row</span>, <span class="hljs-title">List</span>, <span class="hljs-title">Divider</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"antd"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"antd/dist/antd.css"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">React</span>, { <span class="hljs-title">useCallback</span>, <span class="hljs-title">useEffect</span>, <span class="hljs-title">useState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">BrowserRouter</span>, <span class="hljs-title">Link</span>, <span class="hljs-title">Route</span>, <span class="hljs-title">Switch</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Web3Modal</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"web3modal"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Account</span>, <span class="hljs-title">Address</span>, <span class="hljs-title">Balance</span>, <span class="hljs-title">Contract</span>, <span class="hljs-title">Faucet</span>, <span class="hljs-title">GasGauge</span>, <span class="hljs-title">Header</span>, <span class="hljs-title">Ramp</span>, <span class="hljs-title">ThemeSwitch</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./components"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">INFURA_ID</span>, <span class="hljs-title">NETWORK</span>, <span class="hljs-title">NETWORKS</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./constants"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Transactor</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./helpers"</span>;
<span class="hljs-keyword">import</span> {
  <span class="hljs-title">useBalance</span>,
  <span class="hljs-title">useContractLoader</span>,
  <span class="hljs-title">useContractReader</span>,
  <span class="hljs-title">useGasPrice</span>,
  <span class="hljs-title">useOnBlock</span>,
  <span class="hljs-title">useUserProviderAndSigner</span>,
} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"eth-hooks"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useEventListener</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"eth-hooks/events/useEventListener"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useExchangeEthPrice</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"eth-hooks/dapps/dex"</span>;
<span class="hljs-comment">// import Hints from "./Hints";</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">ExampleUI</span>, <span class="hljs-title">Hints</span>, <span class="hljs-title">Subgraph</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./views"</span>;

<span class="hljs-keyword">import</span> { <span class="hljs-title">useContractConfig</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./hooks"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Portis</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@portis/web3"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Fortmatic</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"fortmatic"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Authereum</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"authereum"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">humanizeDuration</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"humanize-duration"</span>;

const { ethers } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"ethers"</span>);
<span class="hljs-comment">/*
    Welcome to 🏗 scaffold-eth !

    Code:
    https://github.com/austintgriffith/scaffold-eth

    Support:
    https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA
    or DM @austingriffith on Twitter or Telegram

    You should get your own Infura.io ID and put it in `constants.js`
    (this is your connection to the main Ethereum network for ENS etc.)


    🌏 EXTERNAL CONTRACTS:
    You can also bring in contract artifacts in `constants.js`
    (and then use the `useExternalContractLoader()` hook!)
*/</span>

<span class="hljs-comment">/// 📡 What chain are your contracts deployed to?</span>
const targetNetwork <span class="hljs-operator">=</span> NETWORKS.localhost; <span class="hljs-comment">// &#x3C;------- select your target frontend network (localhost, rinkeby, xdai, mainnet)</span>

<span class="hljs-comment">// 😬 Sorry for all the console logging</span>
const DEBUG <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
const NETWORKCHECK <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;

<span class="hljs-comment">// 🛰 providers</span>
<span class="hljs-keyword">if</span> (DEBUG) console.log(<span class="hljs-string">"📡 Connecting to Mainnet Ethereum"</span>);
<span class="hljs-comment">// const mainnetProvider = getDefaultProvider("mainnet", { infura: INFURA_ID, etherscan: ETHERSCAN_KEY, quorum: 1 });</span>
<span class="hljs-comment">// const mainnetProvider = new InfuraProvider("mainnet",INFURA_ID);</span>
<span class="hljs-comment">//</span>
<span class="hljs-comment">// attempt to connect to our own scaffold eth rpc and if that fails fall back to infura...</span>
<span class="hljs-comment">// Using StaticJsonRpcProvider as the chainId won't change see https://github.com/ethers-io/ethers.js/issues/901</span>
const scaffoldEthProvider <span class="hljs-operator">=</span> navigator.onLine
  ? <span class="hljs-keyword">new</span> ethers.providers.StaticJsonRpcProvider(<span class="hljs-string">"https://rpc.scaffoldeth.io:48544"</span>)
  : null;
const poktMainnetProvider <span class="hljs-operator">=</span> navigator.onLine
  ? <span class="hljs-keyword">new</span> ethers.providers.StaticJsonRpcProvider(
      <span class="hljs-string">"https://eth-mainnet.gateway.pokt.network/v1/lb/611156b4a585a20035148406"</span>,
    )
  : null;
const mainnetInfura <span class="hljs-operator">=</span> navigator.onLine
  ? <span class="hljs-keyword">new</span> ethers.providers.StaticJsonRpcProvider(<span class="hljs-string">"https://mainnet.infura.io/v3/"</span> <span class="hljs-operator">+</span> INFURA_ID)
  : null;
<span class="hljs-comment">// ( ⚠️ Getting "failed to meet quorum" errors? Check your INFURA_ID</span>

<span class="hljs-comment">// 🏠 Your local provider is usually pointed at your local blockchain</span>
const localProviderUrl <span class="hljs-operator">=</span> targetNetwork.rpcUrl;
<span class="hljs-comment">// as you deploy to other networks you can set REACT_APP_PROVIDER=https://dai.poa.network in packages/react-app/.env</span>
const localProviderUrlFromEnv <span class="hljs-operator">=</span> process.env.REACT_APP_PROVIDER ? process.env.REACT_APP_PROVIDER : localProviderUrl;
<span class="hljs-keyword">if</span> (DEBUG) console.log(<span class="hljs-string">"🏠 Connecting to provider:"</span>, localProviderUrlFromEnv);
const localProvider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.StaticJsonRpcProvider(localProviderUrlFromEnv);

<span class="hljs-comment">// 🔭 block explorer URL</span>
const blockExplorer <span class="hljs-operator">=</span> targetNetwork.blockExplorer;

<span class="hljs-comment">// Coinbase walletLink init</span>
const walletLink <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> WalletLink({
  appName: <span class="hljs-string">"coinbase"</span>,
});

<span class="hljs-comment">// WalletLink provider</span>
const walletLinkProvider <span class="hljs-operator">=</span> walletLink.makeWeb3Provider(`https:<span class="hljs-comment">//mainnet.infura.io/v3/${INFURA_ID}`, 1);</span>

<span class="hljs-comment">// Portis ID: 6255fb2b-58c8-433b-a2c9-62098c05ddc9</span>
<span class="hljs-comment">/*
  Web3 modal helps us "connect" external wallets:
*/</span>
const web3Modal <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Web3Modal({
  network: <span class="hljs-string">"mainnet"</span>, <span class="hljs-comment">// Optional. If using WalletConnect on xDai, change network to "xdai" and add RPC info below for xDai chain.</span>
  cacheProvider: <span class="hljs-literal">true</span>, <span class="hljs-comment">// optional</span>
  theme: <span class="hljs-string">"light"</span>, <span class="hljs-comment">// optional. Change to "dark" for a dark theme.</span>
  providerOptions: {
    walletconnect: {
      package: WalletConnectProvider, <span class="hljs-comment">// required</span>
      options: {
        bridge: <span class="hljs-string">"https://polygon.bridge.walletconnect.org"</span>,
        infuraId: INFURA_ID,
        rpc: {
          <span class="hljs-number">1</span>: `https:<span class="hljs-comment">//mainnet.infura.io/v3/${INFURA_ID}`, // mainnet // For more WalletConnect providers: https://docs.walletconnect.org/quick-start/dapps/web3-provider#required</span>
          <span class="hljs-number">42</span>: `https:<span class="hljs-comment">//kovan.infura.io/v3/${INFURA_ID}`,</span>
          <span class="hljs-number">100</span>: <span class="hljs-string">"https://dai.poa.network"</span>, <span class="hljs-comment">// xDai</span>
        },
      },
    },
    portis: {
      display: {
        logo: <span class="hljs-string">"https://user-images.githubusercontent.com/9419140/128913641-d025bc0c-e059-42de-a57b-422f196867ce.png"</span>,
        name: <span class="hljs-string">"Portis"</span>,
        description: <span class="hljs-string">"Connect to Portis App"</span>,
      },
      package: Portis,
      options: {
        id: <span class="hljs-string">"6255fb2b-58c8-433b-a2c9-62098c05ddc9"</span>,
      },
    },
    fortmatic: {
      package: Fortmatic, <span class="hljs-comment">// required</span>
      options: {
        key: <span class="hljs-string">"pk_live_5A7C91B2FC585A17"</span>, <span class="hljs-comment">// required</span>
      },
    },
    <span class="hljs-comment">// torus: {</span>
    <span class="hljs-comment">//   package: Torus,</span>
    <span class="hljs-comment">//   options: {</span>
    <span class="hljs-comment">//     networkParams: {</span>
    <span class="hljs-comment">//       host: "https://localhost:8545", // optional</span>
    <span class="hljs-comment">//       chainId: 1337, // optional</span>
    <span class="hljs-comment">//       networkId: 1337 // optional</span>
    <span class="hljs-comment">//     },</span>
    <span class="hljs-comment">//     config: {</span>
    <span class="hljs-comment">//       buildEnv: "development" // optional</span>
    <span class="hljs-comment">//     },</span>
    <span class="hljs-comment">//   },</span>
    <span class="hljs-comment">// },</span>
    <span class="hljs-string">"custom-walletlink"</span>: {
      display: {
        logo: <span class="hljs-string">"https://play-lh.googleusercontent.com/PjoJoG27miSglVBXoXrxBSLveV6e3EeBPpNY55aiUUBM9Q1RCETKCOqdOkX2ZydqVf0"</span>,
        name: <span class="hljs-string">"Coinbase"</span>,
        description: <span class="hljs-string">"Connect to Coinbase Wallet (not Coinbase App)"</span>,
      },
      package: walletLinkProvider,
      connector: async (provider, _options) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
        await provider.enable();
        <span class="hljs-keyword">return</span> provider;
      },
    },
    authereum: {
      package: Authereum, <span class="hljs-comment">// required</span>
    },
  },
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params">props</span>) </span>{
  const mainnetProvider <span class="hljs-operator">=</span>
    poktMainnetProvider <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> poktMainnetProvider._isProvider
      ? poktMainnetProvider
      : scaffoldEthProvider <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> scaffoldEthProvider._network
      ? scaffoldEthProvider
      : mainnetInfura;

  const [injectedProvider, setInjectedProvider] <span class="hljs-operator">=</span> useState();
  const [<span class="hljs-keyword">address</span>, setAddress] <span class="hljs-operator">=</span> useState();

  const logoutOfWeb3Modal <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    await web3Modal.clearCachedProvider();
    <span class="hljs-keyword">if</span> (injectedProvider <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> injectedProvider.provider <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> typeof injectedProvider.provider.disconnect <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"function"</span>) {
      await injectedProvider.provider.disconnect();
    }
    setTimeout(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
      window.location.reload();
    }, <span class="hljs-number">1</span>);
  };

  <span class="hljs-comment">/* 💵 This hook will get the price of ETH from 🦄 Uniswap: */</span>
  const price <span class="hljs-operator">=</span> useExchangeEthPrice(targetNetwork, mainnetProvider);

  <span class="hljs-comment">/* 🔥 This hook will get the price of Gas from ⛽️ EtherGasStation */</span>
  const gasPrice <span class="hljs-operator">=</span> useGasPrice(targetNetwork, <span class="hljs-string">"fast"</span>);
  <span class="hljs-comment">// Use your injected provider from 🦊 Metamask or if you don't have it then instantly generate a 🔥 burner wallet.</span>
  const userProviderAndSigner <span class="hljs-operator">=</span> useUserProviderAndSigner(injectedProvider, localProvider);
  const userSigner <span class="hljs-operator">=</span> userProviderAndSigner.signer;

  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAddress</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">if</span> (userSigner) {
        const newAddress <span class="hljs-operator">=</span> await userSigner.getAddress();
        setAddress(newAddress);
      }
    }
    getAddress();
  }, [userSigner]);

  <span class="hljs-comment">// You can warn the user if you would like them to be on a specific network</span>
  const localChainId <span class="hljs-operator">=</span> localProvider <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> localProvider._network <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> localProvider._network.chainId;
  const selectedChainId <span class="hljs-operator">=</span>
    userSigner <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> userSigner.provider <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> userSigner.provider._network <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> userSigner.provider._network.chainId;

  <span class="hljs-comment">// For more hooks, check out 🔗eth-hooks at: https://www.npmjs.com/package/eth-hooks</span>

  <span class="hljs-comment">// The transactor wraps transactions and provides notificiations</span>
  const <span class="hljs-built_in">tx</span> <span class="hljs-operator">=</span> Transactor(userSigner, gasPrice);

  <span class="hljs-comment">// Faucet Tx can be used to send funds from the faucet</span>
  const faucetTx <span class="hljs-operator">=</span> Transactor(localProvider, gasPrice);

  <span class="hljs-comment">// 🏗 scaffold-eth is full of handy hooks like this one to get your balance:</span>
  const yourLocalBalance <span class="hljs-operator">=</span> useBalance(localProvider, <span class="hljs-keyword">address</span>);

  <span class="hljs-comment">// Just plug in different 🛰 providers to get your balance on different chains:</span>
  const yourMainnetBalance <span class="hljs-operator">=</span> useBalance(mainnetProvider, <span class="hljs-keyword">address</span>);

  const contractConfig <span class="hljs-operator">=</span> useContractConfig();

  <span class="hljs-comment">// Load in your local 📝 contract and read a value from it:</span>
  const readContracts <span class="hljs-operator">=</span> useContractLoader(localProvider, contractConfig);

  <span class="hljs-comment">// If you want to make 🔐 write transactions to your contracts, use the userSigner:</span>
  const writeContracts <span class="hljs-operator">=</span> useContractLoader(userSigner, contractConfig, localChainId);

  <span class="hljs-comment">// EXTERNAL CONTRACT EXAMPLE:</span>
  <span class="hljs-comment">//</span>
  <span class="hljs-comment">// If you want to bring in the mainnet DAI contract it would look like:</span>
  const mainnetContracts <span class="hljs-operator">=</span> useContractLoader(mainnetProvider, contractConfig);

  <span class="hljs-comment">// If you want to call a function on a new block</span>
  useOnBlock(mainnetProvider, () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    console.log(`⛓ A <span class="hljs-keyword">new</span> mainnet <span class="hljs-built_in">block</span> <span class="hljs-keyword">is</span> here: ${mainnetProvider._lastBlockNumber}`);
  });

  <span class="hljs-comment">// Then read your DAI balance like:</span>
  const myMainnetDAIBalance <span class="hljs-operator">=</span> useContractReader(mainnetContracts, <span class="hljs-string">"DAI"</span>, <span class="hljs-string">"balanceOf"</span>, [
    <span class="hljs-string">"0x34aA3F359A9D614239015126635CE7732c18fDF3"</span>,
  ]);

  <span class="hljs-comment">//keep track of contract balance to know how much has been staked total:</span>
  const stakerContractBalance <span class="hljs-operator">=</span> useBalance(
    localProvider,
    readContracts <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> readContracts.Staker ? readContracts.Staker.<span class="hljs-built_in">address</span> : null,
  );
  <span class="hljs-keyword">if</span> (DEBUG) console.log(<span class="hljs-string">"💵 stakerContractBalance"</span>, stakerContractBalance);

  const rewardRatePerSecond <span class="hljs-operator">=</span> useContractReader(readContracts, <span class="hljs-string">"Staker"</span>, <span class="hljs-string">"rewardRatePerSecond"</span>);
  console.log(<span class="hljs-string">"💵 Reward Rate:"</span>, rewardRatePerSecond);

  <span class="hljs-comment">// ** keep track of a variable from the contract in the local React state:</span>
  const balanceStaked <span class="hljs-operator">=</span> useContractReader(readContracts, <span class="hljs-string">"Staker"</span>, <span class="hljs-string">"balances"</span>, [<span class="hljs-keyword">address</span>]);
  console.log(<span class="hljs-string">"💸 balanceStaked:"</span>, balanceStaked);

  <span class="hljs-comment">// ** 📟 Listen for broadcast events</span>
  const stakeEvents <span class="hljs-operator">=</span> useEventListener(readContracts, <span class="hljs-string">"Staker"</span>, <span class="hljs-string">"Stake"</span>, localProvider, <span class="hljs-number">1</span>);
  console.log(<span class="hljs-string">"📟 stake events:"</span>, stakeEvents);

  const receiveEvents <span class="hljs-operator">=</span> useEventListener(readContracts, <span class="hljs-string">"Staker"</span>, <span class="hljs-string">"Received"</span>, localProvider, <span class="hljs-number">1</span>);
  console.log(<span class="hljs-string">"📟 receive events:"</span>, receiveEvents);

  <span class="hljs-comment">// ** keep track of a variable from the contract in the local React state:</span>
  const claimPeriodLeft <span class="hljs-operator">=</span> useContractReader(readContracts, <span class="hljs-string">"Staker"</span>, <span class="hljs-string">"claimPeriodLeft"</span>);
  console.log(<span class="hljs-string">"⏳ Claim Period Left:"</span>, claimPeriodLeft);

  const withdrawalTimeLeft <span class="hljs-operator">=</span> useContractReader(readContracts, <span class="hljs-string">"Staker"</span>, <span class="hljs-string">"withdrawalTimeLeft"</span>);
  console.log(<span class="hljs-string">"⏳ Withdrawal Time Left:"</span>, withdrawalTimeLeft);


  <span class="hljs-comment">// ** Listen for when the contract has been 'completed'</span>
  const complete <span class="hljs-operator">=</span> useContractReader(readContracts, <span class="hljs-string">"ExampleExternalContract"</span>, <span class="hljs-string">"completed"</span>);
  console.log(<span class="hljs-string">"✅ complete:"</span>, complete);

  const exampleExternalContractBalance <span class="hljs-operator">=</span> useBalance(
    localProvider,
    readContracts <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> readContracts.ExampleExternalContract ? readContracts.ExampleExternalContract.<span class="hljs-built_in">address</span> : null,
  );
  <span class="hljs-keyword">if</span> (DEBUG) console.log(<span class="hljs-string">"💵 exampleExternalContractBalance"</span>, exampleExternalContractBalance);

  let completeDisplay <span class="hljs-operator">=</span> <span class="hljs-string">""</span>;
  <span class="hljs-keyword">if</span> (complete) {
    completeDisplay <span class="hljs-operator">=</span> (
      <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{padding: <span class="hljs-number">64</span>, backgroundColor: <span class="hljs-string">"#eeffef"</span>, fontWeight: <span class="hljs-string">"bold"</span>, color: <span class="hljs-string">"rgba(0, 0, 0, 0.85)"</span> }} <span class="hljs-operator">></span>
        <span class="hljs-operator">-</span><span class="hljs-operator">-</span> 💀 Staking App Fund Repatriation Executed 🪦 <span class="hljs-operator">-</span><span class="hljs-operator">-</span>
        <span class="hljs-operator">&#x3C;</span>Balance balance<span class="hljs-operator">=</span>{exampleExternalContractBalance} fontSize<span class="hljs-operator">=</span>{<span class="hljs-number">32</span>} <span class="hljs-operator">/</span><span class="hljs-operator">></span> ETH locked<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-comment">/*
  const addressFromENS = useResolveName(mainnetProvider, "austingriffith.eth");
  console.log("🏷 Resolved austingriffith.eth as:", addressFromENS)
  */</span>

  <span class="hljs-comment">//</span>
  <span class="hljs-comment">// 🧫 DEBUG 👨🏻‍🔬</span>
  <span class="hljs-comment">//</span>
  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">if</span> (
      DEBUG <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
      mainnetProvider <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
      <span class="hljs-keyword">address</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
      selectedChainId <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
      yourLocalBalance <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
      yourMainnetBalance <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
      readContracts <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
      writeContracts <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
      mainnetContracts
    ) {
      console.log(<span class="hljs-string">"_____________________________________ 🏗 scaffold-eth _____________________________________"</span>);
      console.log(<span class="hljs-string">"🌎 mainnetProvider"</span>, mainnetProvider);
      console.log(<span class="hljs-string">"🏠 localChainId"</span>, localChainId);
      console.log(<span class="hljs-string">"👩‍💼 selected address:"</span>, <span class="hljs-keyword">address</span>);
      console.log(<span class="hljs-string">"🕵🏻‍♂️ selectedChainId:"</span>, selectedChainId);
      console.log(<span class="hljs-string">"💵 yourLocalBalance"</span>, yourLocalBalance ? ethers.utils.formatEther(yourLocalBalance) : <span class="hljs-string">"..."</span>);
      console.log(<span class="hljs-string">"💵 yourMainnetBalance"</span>, yourMainnetBalance ? ethers.utils.formatEther(yourMainnetBalance) : <span class="hljs-string">"..."</span>);
      console.log(<span class="hljs-string">"📝 readContracts"</span>, readContracts);
      console.log(<span class="hljs-string">"🌍 DAI contract on mainnet:"</span>, mainnetContracts);
      console.log(<span class="hljs-string">"💵 yourMainnetDAIBalance"</span>, myMainnetDAIBalance);
      console.log(<span class="hljs-string">"🔐 writeContracts"</span>, writeContracts);
    }
  }, [
    mainnetProvider,
    <span class="hljs-keyword">address</span>,
    selectedChainId,
    yourLocalBalance,
    yourMainnetBalance,
    readContracts,
    writeContracts,
    mainnetContracts,
  ]);

  let networkDisplay <span class="hljs-operator">=</span> <span class="hljs-string">""</span>;
  <span class="hljs-keyword">if</span> (NETWORKCHECK <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> localChainId <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> selectedChainId <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> localChainId <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> selectedChainId) {
    const networkSelected <span class="hljs-operator">=</span> NETWORK(selectedChainId);
    const networkLocal <span class="hljs-operator">=</span> NETWORK(localChainId);
    <span class="hljs-keyword">if</span> (selectedChainId <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">1337</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> localChainId <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">31337</span>) {
      networkDisplay <span class="hljs-operator">=</span> (
        <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ zIndex: <span class="hljs-number">2</span>, position: <span class="hljs-string">"absolute"</span>, right: <span class="hljs-number">0</span>, top: <span class="hljs-number">60</span>, padding: <span class="hljs-number">16</span> }}<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Alert
            message<span class="hljs-operator">=</span><span class="hljs-string">"⚠️ Wrong Network ID"</span>
            description<span class="hljs-operator">=</span>{
              <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
                You have <span class="hljs-operator">&#x3C;</span>b<span class="hljs-operator">></span>chain id <span class="hljs-number">1337</span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>b<span class="hljs-operator">></span> <span class="hljs-keyword">for</span> localhost and you need to change it to <span class="hljs-operator">&#x3C;</span>b<span class="hljs-operator">></span><span class="hljs-number">31337</span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>b<span class="hljs-operator">></span> to work with
                HardHat.
                &#x3C;div<span class="hljs-operator">></span>(MetaMask <span class="hljs-operator">-</span><span class="hljs-operator">&#x26;</span>gt; Settings <span class="hljs-operator">-</span><span class="hljs-operator">&#x26;</span>gt; Networks <span class="hljs-operator">-</span><span class="hljs-operator">&#x26;</span>gt; Chain ID <span class="hljs-operator">-</span><span class="hljs-operator">&#x26;</span>gt; <span class="hljs-number">31337</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-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"error"</span>
            closable<span class="hljs-operator">=</span>{<span class="hljs-literal">false</span>}
          <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      );
    } <span class="hljs-keyword">else</span> {
      networkDisplay <span class="hljs-operator">=</span> (
        <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ zIndex: <span class="hljs-number">2</span>, position: <span class="hljs-string">"absolute"</span>, right: <span class="hljs-number">0</span>, top: <span class="hljs-number">60</span>, padding: <span class="hljs-number">16</span> }}<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Alert
            message<span class="hljs-operator">=</span><span class="hljs-string">"⚠️ Wrong Network"</span>
            description<span class="hljs-operator">=</span>{
              <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
                You have <span class="hljs-operator">&#x3C;</span>b<span class="hljs-operator">></span>{networkSelected <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> networkSelected.<span class="hljs-built_in">name</span>}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>b<span class="hljs-operator">></span> selected and you need to be on{<span class="hljs-string">" "</span>}
                <span class="hljs-operator">&#x3C;</span>Button
                  onClick<span class="hljs-operator">=</span>{async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
                    const ethereum <span class="hljs-operator">=</span> window.ethereum;
                    const data <span class="hljs-operator">=</span> [
                      {
                        chainId: <span class="hljs-string">"0x"</span> <span class="hljs-operator">+</span> targetNetwork.chainId.toString(<span class="hljs-number">16</span>),
                        chainName: targetNetwork.<span class="hljs-built_in">name</span>,
                        nativeCurrency: targetNetwork.nativeCurrency,
                        rpcUrls: [targetNetwork.rpcUrl],
                        blockExplorerUrls: [targetNetwork.blockExplorer],
                      },
                    ];
                    console.log(<span class="hljs-string">"data"</span>, data);

                    let switchTx;
                    <span class="hljs-comment">// https://docs.metamask.io/guide/rpc-api.html#other-rpc-methods</span>
                    <span class="hljs-keyword">try</span> {
                      switchTx <span class="hljs-operator">=</span> await ethereum.request({
                        method: <span class="hljs-string">"wallet_switchEthereumChain"</span>,
                        params: [{ chainId: data[<span class="hljs-number">0</span>].chainId }],
                      });
                    } <span class="hljs-keyword">catch</span> (switchError) {
                      <span class="hljs-comment">// not checking specific error code, because maybe we're not using MetaMask</span>
                      <span class="hljs-keyword">try</span> {
                        switchTx <span class="hljs-operator">=</span> await ethereum.request({
                          method: <span class="hljs-string">"wallet_addEthereumChain"</span>,
                          params: data,
                        });
                      } <span class="hljs-keyword">catch</span> (addError) {
                        <span class="hljs-comment">// handle "add" error</span>
                      }
                    }

                    <span class="hljs-keyword">if</span> (switchTx) {
                      console.log(switchTx);
                    }
                  }}
                <span class="hljs-operator">></span>
                  <span class="hljs-operator">&#x3C;</span>b<span class="hljs-operator">></span>{networkLocal <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> networkLocal.<span class="hljs-built_in">name</span>}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>b<span class="hljs-operator">></span>
                <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
            }
            <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"error"</span>
            closable<span class="hljs-operator">=</span>{<span class="hljs-literal">false</span>}
          <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      );
    }
  } <span class="hljs-keyword">else</span> {
    networkDisplay <span class="hljs-operator">=</span> (
      <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ zIndex: <span class="hljs-number">-1</span>, position: <span class="hljs-string">"absolute"</span>, right: <span class="hljs-number">154</span>, top: <span class="hljs-number">28</span>, padding: <span class="hljs-number">16</span>, color: targetNetwork.color }}<span class="hljs-operator">></span>
        {targetNetwork.<span class="hljs-built_in">name</span>}
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    );
  }

  const loadWeb3Modal <span class="hljs-operator">=</span> useCallback(async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const provider <span class="hljs-operator">=</span> await web3Modal.connect();
    setInjectedProvider(<span class="hljs-keyword">new</span> ethers.providers.Web3Provider(provider));

    provider.on(<span class="hljs-string">"chainChanged"</span>, chainId <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
      console.log(`chain changed to ${chainId}<span class="hljs-operator">!</span> updating providers`);
      setInjectedProvider(<span class="hljs-keyword">new</span> ethers.providers.Web3Provider(provider));
    });

    provider.on(<span class="hljs-string">"accountsChanged"</span>, () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
      console.log(`account changed<span class="hljs-operator">!</span>`);
      setInjectedProvider(<span class="hljs-keyword">new</span> ethers.providers.Web3Provider(provider));
    });

    <span class="hljs-comment">// Subscribe to session disconnection</span>
    provider.on(<span class="hljs-string">"disconnect"</span>, (code, reason) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
      console.log(code, reason);
      logoutOfWeb3Modal();
    });
  }, [setInjectedProvider]);

  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">if</span> (web3Modal.cachedProvider) {
      loadWeb3Modal();
    }
  }, [loadWeb3Modal]);

  const [route, setRoute] <span class="hljs-operator">=</span> useState();
  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    setRoute(window.location.pathname);
  }, [setRoute]);

  let faucetHint <span class="hljs-operator">=</span> <span class="hljs-string">""</span>;
  const faucetAvailable <span class="hljs-operator">=</span> localProvider <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> localProvider.connection <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> targetNetwork.<span class="hljs-built_in">name</span>.indexOf(<span class="hljs-string">"local"</span>) <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">-1</span>;

  const [faucetClicked, setFaucetClicked] <span class="hljs-operator">=</span> useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">if</span> (
    <span class="hljs-operator">!</span>faucetClicked <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
    localProvider <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
    localProvider._network <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
    localProvider._network.chainId <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">31337</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
    yourLocalBalance <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
    ethers.utils.formatEther(yourLocalBalance) <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>
  ) {
    faucetHint <span class="hljs-operator">=</span> (
      <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">16</span> }}<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Button
          <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"primary"</span>
          onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
            faucetTx({
              to: <span class="hljs-keyword">address</span>,
              <span class="hljs-built_in">value</span>: ethers.utils.parseEther(<span class="hljs-string">"0.01"</span>),
            });
            setFaucetClicked(<span class="hljs-literal">true</span>);
          }}
        <span class="hljs-operator">></span>
          💰 Grab funds <span class="hljs-keyword">from</span> the faucet ⛽️
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<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">"App"</span><span class="hljs-operator">></span>
      {<span class="hljs-comment">/* ✏️ Edit the header and change the title to your project name */</span>}
      <span class="hljs-operator">&#x3C;</span>Header <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      {networkDisplay}
      <span class="hljs-operator">&#x3C;</span>BrowserRouter<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Menu style<span class="hljs-operator">=</span>{{ textAlign: <span class="hljs-string">"center"</span> }} selectedKeys<span class="hljs-operator">=</span>{[route]} mode<span class="hljs-operator">=</span><span class="hljs-string">"horizontal"</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Menu.Item key<span class="hljs-operator">=</span><span class="hljs-string">"/"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Link
              onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
                setRoute(<span class="hljs-string">"/"</span>);
              }}
              to<span class="hljs-operator">=</span><span class="hljs-string">"/"</span>
            <span class="hljs-operator">></span>
              Staker UI
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Menu.Item>
          <span class="hljs-operator">&#x3C;</span>Menu.Item key<span class="hljs-operator">=</span><span class="hljs-string">"/contracts"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Link
              onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
                setRoute(<span class="hljs-string">"/contracts"</span>);
              }}
              to<span class="hljs-operator">=</span><span class="hljs-string">"/contracts"</span>
            <span class="hljs-operator">></span>
              Debug Contracts
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Menu.Item>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Menu<span class="hljs-operator">></span>

        <span class="hljs-operator">&#x3C;</span>Switch<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Route exact path<span class="hljs-operator">=</span><span class="hljs-string">"/"</span><span class="hljs-operator">></span>
            {completeDisplay}

            <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">8</span>, marginTop: <span class="hljs-number">16</span> }}<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>Staker Contract:<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>Address value<span class="hljs-operator">=</span>{readContracts <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> readContracts.Staker <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> readContracts.Staker.<span class="hljs-built_in">address</span>} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>Divider <span class="hljs-operator">/</span><span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">8</span>, marginTop: <span class="hljs-number">16</span> }}<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>Reward Rate Per Second:<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>Balance balance<span class="hljs-operator">=</span>{rewardRatePerSecond} fontSize<span class="hljs-operator">=</span>{<span class="hljs-number">64</span>} <span class="hljs-operator">/</span><span class="hljs-operator">></span> ETH
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>Divider <span class="hljs-operator">/</span><span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">8</span>, marginTop: <span class="hljs-number">16</span>, fontWeight: <span class="hljs-string">"bold"</span> }}<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>Claim Period Left:<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              {claimPeriodLeft <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> humanizeDuration(claimPeriodLeft.toNumber() <span class="hljs-operator">*</span> <span class="hljs-number">1000</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 style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">8</span>, marginTop: <span class="hljs-number">16</span>, fontWeight: <span class="hljs-string">"bold"</span>}}<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>Withdrawal Period Left:<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              {withdrawalTimeLeft <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> humanizeDuration(withdrawalTimeLeft.toNumber() <span class="hljs-operator">*</span> <span class="hljs-number">1000</span>)}
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>Divider <span class="hljs-operator">/</span><span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">8</span>, fontWeight: <span class="hljs-string">"bold"</span>}}<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>Total Available ETH in Contract:<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>Balance balance<span class="hljs-operator">=</span>{stakerContractBalance} fontSize<span class="hljs-operator">=</span>{<span class="hljs-number">64</span>} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>Divider <span class="hljs-operator">/</span><span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">8</span>,fontWeight: <span class="hljs-string">"bold"</span> }}<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>ETH Locked 🔒 in Staker Contract:<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>Balance balance<span class="hljs-operator">=</span>{balanceStaked} fontSize<span class="hljs-operator">=</span>{<span class="hljs-number">64</span>} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">8</span> }}<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>Button
                <span class="hljs-keyword">type</span><span class="hljs-operator">=</span>{<span class="hljs-string">"default"</span>}
                onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
                  <span class="hljs-built_in">tx</span>(writeContracts.Staker.execute());
                }}
              <span class="hljs-operator">></span>
                📡 Execute<span class="hljs-operator">!</span>
              <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">8</span> }}<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>Button
                <span class="hljs-keyword">type</span><span class="hljs-operator">=</span>{<span class="hljs-string">"default"</span>}
                onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
                  <span class="hljs-built_in">tx</span>(writeContracts.Staker.withdraw());
                }}
              <span class="hljs-operator">></span>
                🏧 Withdraw
              <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">8</span> }}<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>Button
                <span class="hljs-keyword">type</span><span class="hljs-operator">=</span>{balanceStaked ? <span class="hljs-string">"success"</span> : <span class="hljs-string">"primary"</span>}
                onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
                  <span class="hljs-built_in">tx</span>(writeContracts.Staker.stake({ <span class="hljs-built_in">value</span>: ethers.utils.parseEther(<span class="hljs-string">"0.5"</span>) }));
                }}
              <span class="hljs-operator">></span>
                🥩 Stake <span class="hljs-number">0</span><span class="hljs-number">.5</span> <span class="hljs-literal">ether</span><span class="hljs-operator">!</span>
              <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

            {<span class="hljs-comment">/*
                🎛 this scaffolding is full of commonly used components
                this &#x3C;Contract/> component will automatically parse your ABI
                and give you a form to interact with it locally
            */</span>}

            {<span class="hljs-comment">/* uncomment for a second contract:
            &#x3C;Contract
              name="SecondContract"
              signer={userProvider.getSigner()}
              provider={localProvider}
              address={address}
              blockExplorer={blockExplorer}
              contractConfig={contractConfig}
            />
            */</span>}
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Route<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Route path<span class="hljs-operator">=</span><span class="hljs-string">"/contracts"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Contract
              name<span class="hljs-operator">=</span><span class="hljs-string">"Staker"</span>
              signer<span class="hljs-operator">=</span>{userSigner}
              provider<span class="hljs-operator">=</span>{localProvider}
              <span class="hljs-keyword">address</span><span class="hljs-operator">=</span>{<span class="hljs-keyword">address</span>}
              blockExplorer<span class="hljs-operator">=</span>{blockExplorer}
              contractConfig<span class="hljs-operator">=</span>{contractConfig}
            <span class="hljs-operator">/</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Contract
              name<span class="hljs-operator">=</span><span class="hljs-string">"ExampleExternalContract"</span>
              signer<span class="hljs-operator">=</span>{userSigner}
              provider<span class="hljs-operator">=</span>{localProvider}
              <span class="hljs-keyword">address</span><span class="hljs-operator">=</span>{<span class="hljs-keyword">address</span>}
              blockExplorer<span class="hljs-operator">=</span>{blockExplorer}
              contractConfig<span class="hljs-operator">=</span>{contractConfig}
            <span class="hljs-operator">/</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Route<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Switch<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>BrowserRouter<span class="hljs-operator">></span>

      <span class="hljs-operator">&#x3C;</span>ThemeSwitch <span class="hljs-operator">/</span><span class="hljs-operator">></span>

      {<span class="hljs-comment">/* 👨‍💼 Your account is in the top right with a wallet at connect options */</span>}
      <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ position: <span class="hljs-string">"fixed"</span>, textAlign: <span class="hljs-string">"right"</span>, right: <span class="hljs-number">0</span>, top: <span class="hljs-number">0</span>, padding: <span class="hljs-number">10</span> }}<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Account
          <span class="hljs-keyword">address</span><span class="hljs-operator">=</span>{<span class="hljs-keyword">address</span>}
          localProvider<span class="hljs-operator">=</span>{localProvider}
          userSigner<span class="hljs-operator">=</span>{userSigner}
          mainnetProvider<span class="hljs-operator">=</span>{mainnetProvider}
          price<span class="hljs-operator">=</span>{price}
          web3Modal<span class="hljs-operator">=</span>{web3Modal}
          loadWeb3Modal<span class="hljs-operator">=</span>{loadWeb3Modal}
          logoutOfWeb3Modal<span class="hljs-operator">=</span>{logoutOfWeb3Modal}
          blockExplorer<span class="hljs-operator">=</span>{blockExplorer}
        <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        {faucetHint}
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

      <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ marginTop: <span class="hljs-number">32</span>, opacity: <span class="hljs-number">0</span><span class="hljs-number">.5</span> }}<span class="hljs-operator">></span>
        {<span class="hljs-comment">/* Add your address here */</span>}
        Created by <span class="hljs-operator">&#x3C;</span>Address value<span class="hljs-operator">=</span>{<span class="hljs-string">"Your...address"</span>} ensProvider<span class="hljs-operator">=</span>{mainnetProvider} fontSize<span class="hljs-operator">=</span>{<span class="hljs-number">16</span>} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

      <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ marginTop: <span class="hljs-number">32</span>, opacity: <span class="hljs-number">0</span><span class="hljs-number">.5</span> }}<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>a target<span class="hljs-operator">=</span><span class="hljs-string">"_blank"</span> style<span class="hljs-operator">=</span>{{ padding: <span class="hljs-number">32</span>, color: <span class="hljs-string">"#000"</span> }} href<span class="hljs-operator">=</span><span class="hljs-string">"https://github.com/scaffold-eth/scaffold-eth"</span><span class="hljs-operator">></span>
          🍴 Fork me<span class="hljs-operator">!</span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<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-comment">/* 🗺 Extra UI like gas price, eth price, faucet, and support: */</span>}
      <span class="hljs-operator">&#x3C;</span>div style<span class="hljs-operator">=</span>{{ position: <span class="hljs-string">"fixed"</span>, textAlign: <span class="hljs-string">"left"</span>, left: <span class="hljs-number">0</span>, bottom: <span class="hljs-number">20</span>, padding: <span class="hljs-number">10</span> }}<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Row align<span class="hljs-operator">=</span><span class="hljs-string">"middle"</span> gutter<span class="hljs-operator">=</span>{[<span class="hljs-number">4</span>, <span class="hljs-number">4</span>]}<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Col span<span class="hljs-operator">=</span>{<span class="hljs-number">8</span>}<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Ramp price<span class="hljs-operator">=</span>{price} <span class="hljs-keyword">address</span><span class="hljs-operator">=</span>{<span class="hljs-keyword">address</span>} networks<span class="hljs-operator">=</span>{NETWORKS} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Col<span class="hljs-operator">></span>

          <span class="hljs-operator">&#x3C;</span>Col span<span class="hljs-operator">=</span>{<span class="hljs-number">8</span>} style<span class="hljs-operator">=</span>{{ textAlign: <span class="hljs-string">"center"</span>, opacity: <span class="hljs-number">0</span><span class="hljs-number">.8</span> }}<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>GasGauge gasPrice<span class="hljs-operator">=</span>{gasPrice} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Col<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Col span<span class="hljs-operator">=</span>{<span class="hljs-number">8</span>} style<span class="hljs-operator">=</span>{{ textAlign: <span class="hljs-string">"center"</span>, opacity: <span class="hljs-number">1</span> }}<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Button
              onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
                window.open(<span class="hljs-string">"https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA"</span>);
              }}
              size<span class="hljs-operator">=</span><span class="hljs-string">"large"</span>
              shape<span class="hljs-operator">=</span><span class="hljs-string">"round"</span>
            <span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>span style<span class="hljs-operator">=</span>{{ marginRight: <span class="hljs-number">8</span> }} role<span class="hljs-operator">=</span><span class="hljs-string">"img"</span> aria<span class="hljs-operator">-</span>label<span class="hljs-operator">=</span><span class="hljs-string">"support"</span><span class="hljs-operator">></span>
                💬
              <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>span<span class="hljs-operator">></span>
              Support
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Col<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Row<span class="hljs-operator">></span>

        <span class="hljs-operator">&#x3C;</span>Row align<span class="hljs-operator">=</span><span class="hljs-string">"middle"</span> gutter<span class="hljs-operator">=</span>{[<span class="hljs-number">4</span>, <span class="hljs-number">4</span>]}<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Col span<span class="hljs-operator">=</span>{<span class="hljs-number">24</span>}<span class="hljs-operator">></span>
            {
              <span class="hljs-comment">/*  if the local provider has a signer, let's show the faucet:  */</span>
              faucetAvailable ? (
                <span class="hljs-operator">&#x3C;</span>Faucet localProvider<span class="hljs-operator">=</span>{localProvider} price<span class="hljs-operator">=</span>{price} ensProvider<span class="hljs-operator">=</span>{mainnetProvider} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
              ) : (
                <span class="hljs-string">""</span>
              )
            }
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Col<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Row<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>
  );
}

export default App;
</code></pre><h3 id="h-3" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.部署合约</h3><p>当我们将上面的代码修改完毕后，后续可以我们将合约再次部署更新下。</p><pre data-type="codeBlock" text="# 控制台输入下面的命令
yarn deploy --reset
"><code><span class="hljs-comment"># 控制台输入下面的命令</span>
yarn deploy --<span class="hljs-keyword">reset</span>
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/cd5365989c15961dbd40e893cdea3b0b780923ef5aab1a0dff59624a3fbc12c7.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>打开一个新的tab页面</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3f0f1ac788022715f63a945759d530a6ec5977d51225173aa4b96a2a89c1e34e.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>我们就可以体验下这个自己制作的DAPP了。</p><p>最后一步也是最重要的一步，将你修改的代码提交到你自己的github仓库。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0c36df928e41122901f62e99226207b3ece3d74cf45db896e3494f875e03073f.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>sheepswap@newsletter.paragraph.com (Robby)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3第五周文本教程- 使用Chainlink构建一个动态的NFT]]></title>
            <link>https://paragraph.com/@sheepswap/alchemy-the-road-to-web3-chainlink-nft</link>
            <guid>9hJNUOwC7w4TPJT4UgkV</guid>
            <pubDate>Thu, 22 Sep 2022 16:19:15 GMT</pubDate>
            <description><![CDATA[什么是动态 NFT？一个动态 NFT是一种不可替代的代币，可以根据特定情况进行更改。 例如，目前有八种不同的LaMelo Ball NFT，每个 NFT 都会记录一组不同的 LaMelo 球员统计数据，从篮板和助攻到得分，并根据这些数据进行更改（10 次助攻？不同颜色 - 得分 1 分，不同球）。 动态 NFT 持有者可以根据 LaMelo 的持续表现获得抽奖和其他 NFT 特定福利的特殊访问权限。它变得更加凉爽；这八个 NFT 之一，Gold Evolve NFT，带来了一个独特的承诺： 如果拉梅洛·鲍尔（LaMelo Ball）赢得了 2021 年 NBA 赛季的年度最佳新秀，NFT 本身就会发展以反映新的形象。LaMelo 赢得了奖项，NFT 得到了发展。我们能学到什么？在本教程中，将学会使用Chainlink 的去中心化和加密保护的预言机网络获取和跟踪资产价格数据，将学会使用来自Chainlink 守护者网络自动化NFT 智能合约，以根据正在跟踪的资产价格数据更新 NFT。如果市场价格上涨，智能合约将随机选择 NFT 的 URI 指向这三个看涨图像之一，并且 NFT 将动...]]></description>
            <content:encoded><![CDATA[<h2 id="h-nft" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">什么是动态 NFT？</h2><p>一个<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.chain.link/what-is-a-dynamic-nft/">动态 NFT</a>是一种不可替代的代币，可以根据特定情况进行更改。</p><p>例如，目前有八种不同的<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://lameloball.io/#/">LaMelo Ball NFT</a>，每个 NFT 都会记录一组不同的 LaMelo 球员统计数据，从篮板和助攻到得分，并根据这些数据进行更改（10 次助攻？不同颜色 - 得分 1 分，不同球）。</p><p>动态 NFT 持有者可以根据 LaMelo 的持续表现获得抽奖和其他 NFT 特定福利的特殊访问权限。它变得更加凉爽；这八个 NFT 之一，Gold Evolve NFT，带来了一个独特的承诺：</p><p>如果拉梅洛·鲍尔（LaMelo Ball）赢得了 2021 年 NBA 赛季的年度最佳新秀，NFT 本身就会发展以反映新的形象。LaMelo 赢得了奖项，NFT 得到了发展。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">我们能学到什么？</h2><p>在本教程中，将学会使用<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://chain.link/">Chainlink 的去中心化和加密保护的预言机网络</a>获取和跟踪资产价格数据，将学会使用来自<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/docs/chainlink-keepers/introduction/">Chainlink 守护者网络</a>自动化NFT 智能合约，以根据正在跟踪的资产价格数据更新 NFT。如果市场价格上涨，智能合约将随机选择 NFT 的 URI 指向这三个看涨图像之一，并且 NFT 将动态更新。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">需要的工具和条件</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://remix.ethereum.org/">Remix IDE</a>和London VM</p></li><li><p>参考github地址：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/zeuslawyer/chainlink-dynamic-nft-alchemy">https://github.com/zeuslawyer/chainlink-dynamic-nft-alchemy</a></p></li><li><p>IPFS，可以使用pinta，帝哥对此提到的工具</p></li><li><p>龙头和测试网代币：我们本次使用的网络是Rinkeby，一旦你的钱包连接到 Rinkeby，<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://rinkebyfaucet.com/">从 Alchemy 的 Rinkeby 水龙头获得 Rinkeby ETH</a>.还需要<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://faucets.chain.link/">获取测试网 LINK 代币</a>.</p><p>对于本次课程的任务，将添加随机性，但将部署到以太坊的 Goerli 测试网。</p><p>如果需要 Goerli 测试网代币，<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://goerlifaucet.com/">从 Alchemy 的 Goerli 水龙头获取 Goerli ETH</a>.</p></li></ul><h2 id="h-1" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1.获取测试币</h2><p>进入 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://faucets.chain.link/">https://faucets.chain.link/</a> 连接钱包，点击Send Request获取测试以太坊和link 代币。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b42e9660376285f90248371d03a9d00f60c6711d05d70854615e3dcaae20b215.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>如果你的钱包没有link这个代币显示，点击导入代币，输入合约地址（</p><p>0x01BE23585060835E02B77ef475b0Cc51aA1e0709）即可：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c71d0f6efb78fb6bcc7acd5826a5106a4601ebfbd9974e26c1c8112e1edf8841.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://vrf.chain.link/rinkeby">https://vrf.chain.link/rinkeby</a> 连接你的钱包，点击Create Subscription，来获取一个Link 预言机的订阅，订阅需要消耗少量gas费。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/65e9c24b572c20703affb6776ae55008d3c9b7d4eb2274be1a5781dedf57049b.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>等待完成后别急着关页面，我们给他提供一些资金。点击add funds。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d4699fe836ec4ff41416cdabb89ad97889bcce2a80b240947e6c4cb8e2201e5c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-2" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.初始化项目</h2><p>进入 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://remix.ethereum.org/">https://remix.ethereum.org/</a> ，开始我们今天的项目开发,新建一个sol文件，结构和代码如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/83367b4934cbd70bca565f4770f726fa570029ce4fdc1070f57219bd8888e163.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>代码如下：</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.4;

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

// Chainlink Imports
import &quot;@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol&quot;;
// This import includes functions from both ./KeeperBase.sol and
// ./interfaces/KeeperCompatibleInterface.sol
import &quot;@chainlink/contracts/src/v0.8/KeeperCompatible.sol&quot;;

import &quot;@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol&quot;;
import &quot;@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol&quot;;

// Dev imports. This only works on a local dev network
// and will not work on any test or main livenets.
import &quot;hardhat/console.sol&quot;;

contract BullBear is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable, VRFConsumerBaseV2, KeeperCompatibleInterface  {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;
    uint public interval;
    uint public lastTimeStamp;

    AggregatorV3Interface public priceFeed;
    int256 public currentPrice;

    // IPFS URIs for the dynamic nft graphics/metadata.
    // NOTE: These connect to my IPFS Companion node.
    // You should upload the contents of the /ipfs folder to your own node for development.
    string[] bullUrisIpfs = [
        &quot;https://ipfs.io/ipfs/QmRXyfi3oNZCubDxiVFre3kLZ8XeGt6pQsnAQRZ7akhSNs?filename=gamer_bull.json&quot;,
        &quot;https://ipfs.io/ipfs/QmRJVFeMrtYS2CUVUM2cHJpBV5aX2xurpnsfZxLTTQbiD3?filename=party_bull.json&quot;,
        &quot;https://ipfs.io/ipfs/QmdcURmN1kEEtKgnbkVJJ8hrmsSWHpZvLkRgsKKoiWvW9g?filename=simple_bull.json&quot;
    ];
    string[] bearUrisIpfs = [
        &quot;https://ipfs.io/ipfs/Qmdx9Hx7FCDZGExyjLR6vYcnutUR8KhBZBnZfAPHiUommN?filename=beanie_bear.json&quot;,
        &quot;https://ipfs.io/ipfs/QmTVLyTSuiKGUEmb88BgXG3qNC8YgpHZiFbjHrXKH3QHEu?filename=coolio_bear.json&quot;,
        &quot;https://ipfs.io/ipfs/QmbKhBXVWmwrYsTPFYfroR2N7NAekAMxHUVg2CWks7i9qj?filename=simple_bear.json&quot;
    ];


    // random
    VRFCoordinatorV2Interface COORDINATOR;

    // Your subscription ID.
    uint64 s_subscriptionId;

    // Goerli coordinator. For other networks,
    // see https://docs.chain.link/docs/vrf-contracts/#configurations
    address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab;

    // The gas lane to use, which specifies the maximum gas price to bump to.
    // For a list of available gas lanes on each network,
    // see https://docs.chain.link/docs/vrf-contracts/#configurations
    bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc;

    // Depends on the number of requested values that you want sent to the
    // fulfillRandomWords() function. Storing each word costs about 20,000 gas,
    // so 100,000 is a safe default for this example contract. Test and adjust
    // this limit based on the network that you select, the size of the request,
    // and the processing of the callback request in the fulfillRandomWords()
    // function.
    uint32 callbackGasLimit = 100000;

    // The default is 3, but you can set this higher.
    uint16 requestConfirmations = 3;

    // For this example, retrieve 2 random values in one request.
    // Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.
    uint32 numWords =  2;
    uint256[] public s_randomWords;
    uint256 public s_requestId;

    event TokensUpdated(string marketTrend);

    constructor(uint updateInterval, address _priceFeed, uint64 subscriptionId) ERC721(&quot;Bull&amp;Bear&quot;, &quot;BBTK&quot;) VRFConsumerBaseV2(vrfCoordinator) {
        interval = updateInterval;
        lastTimeStamp = block.timestamp;

        // https://rinkeby.etherscan.io/address/0xECe365B379E1dD183B20fc5f022230C044d51404
        priceFeed = AggregatorV3Interface(_priceFeed);
        currentPrice = getLatestPrice();

        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        s_subscriptionId = subscriptionId;
    }

    function safeMint(address to) public {
        // Current counter value will be the minted token&apos;s token ID.
        uint256 tokenId = _tokenIdCounter.current();

        // Increment it so next time it&apos;s correct when we call .current()
        _tokenIdCounter.increment();

        // Mint the token
        _safeMint(to, tokenId);

        // Default to a bull NFT
        string memory defaultUri = bullUrisIpfs[s_randomWords[0]%3];
        _setTokenURI(tokenId, defaultUri);

        console.log(
            &quot;DONE!!! minted token &quot;,
            tokenId,
            &quot; and assigned token url: &quot;,
            defaultUri
        );
    }

    function checkUpkeep(bytes calldata) external view override returns (bool upkeepNeeded, bytes memory /*performData*/){
        upkeepNeeded = (block.timestamp - lastTimeStamp) &gt; interval;
    }

    function performUpkeep(bytes calldata) external override{
        if((block.timestamp - lastTimeStamp) &gt; interval){
            lastTimeStamp = block.timestamp;
            int latestPrice = getLatestPrice();

            if(latestPrice == currentPrice){
                return;
            }else if(latestPrice &lt; currentPrice){
                updateAllTokenUris(&quot;bears&quot;);
            }else{
                updateAllTokenUris(&quot;bull&quot;);
            }

            currentPrice = latestPrice;
        }
    }

    function getLatestPrice() public view returns(int256){
        (,
        int price,
        ,
        ,) = priceFeed.latestRoundData();
        return price;
    }

    function updateAllTokenUris(string memory trend) internal{
        if(compareStrings(&quot;bears&quot;, trend)){
            for(uint i=0; i&lt; _tokenIdCounter.current(); i++){
                _setTokenURI(i,bearUrisIpfs[s_randomWords[0]%3]);
            }
        }else {
            for(uint i=0; i&lt; _tokenIdCounter.current(); i++){
                _setTokenURI(i,bullUrisIpfs[s_randomWords[0]%3]);
            }
        }

        emit TokensUpdated(trend);
    }

    function setInterval(uint256 newInterval) public onlyOwner{
        interval = newInterval;
    }

    function setPriceFeed(address newFeed) public onlyOwner{
        priceFeed = AggregatorV3Interface(newFeed);
    }

    function compareStrings(string memory a, string memory b) internal pure returns (bool){
        return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
    }

    // The following functions are overrides required by Solidity.
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId)
        internal
        override(ERC721, ERC721URIStorage)
    {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

        // Assumes the subscription is funded sufficiently.
    function requestRandomWords() external onlyOwner {
        // Will revert if subscription is not set and funded.
        s_requestId = COORDINATOR.requestRandomWords(
        keyHash,
        s_subscriptionId,
        requestConfirmations,
        callbackGasLimit,
        numWords
        );
    }

    function fulfillRandomWords(
        uint256, /* requestId */
        uint256[] memory randomWords
    ) internal override {
        s_randomWords = randomWords;
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: GPL-3.0</span>

<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.4;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/ERC721.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.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-comment">// Chainlink Imports</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"</span>;
<span class="hljs-comment">// This import includes functions from both ./KeeperBase.sol and</span>
<span class="hljs-comment">// ./interfaces/KeeperCompatibleInterface.sol</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"@chainlink/contracts/src/v0.8/KeeperCompatible.sol"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">"@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"</span>;

<span class="hljs-comment">// Dev imports. This only works on a local dev network</span>
<span class="hljs-comment">// and will not work on any test or main livenets.</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"hardhat/console.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">BullBear</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC721</span>, <span class="hljs-title">ERC721Enumerable</span>, <span class="hljs-title">ERC721URIStorage</span>, <span class="hljs-title">Ownable</span>, <span class="hljs-title">VRFConsumerBaseV2</span>, <span class="hljs-title">KeeperCompatibleInterface</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> _tokenIdCounter;
    <span class="hljs-keyword">uint</span> <span class="hljs-keyword">public</span> interval;
    <span class="hljs-keyword">uint</span> <span class="hljs-keyword">public</span> lastTimeStamp;

    AggregatorV3Interface <span class="hljs-keyword">public</span> priceFeed;
    <span class="hljs-keyword">int256</span> <span class="hljs-keyword">public</span> currentPrice;

    <span class="hljs-comment">// IPFS URIs for the dynamic nft graphics/metadata.</span>
    <span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> These connect to my IPFS Companion node.</span>
    <span class="hljs-comment">// You should upload the contents of the /ipfs folder to your own node for development.</span>
    <span class="hljs-keyword">string</span>[] bullUrisIpfs <span class="hljs-operator">=</span> [
        <span class="hljs-string">"https://ipfs.io/ipfs/QmRXyfi3oNZCubDxiVFre3kLZ8XeGt6pQsnAQRZ7akhSNs?filename=gamer_bull.json"</span>,
        <span class="hljs-string">"https://ipfs.io/ipfs/QmRJVFeMrtYS2CUVUM2cHJpBV5aX2xurpnsfZxLTTQbiD3?filename=party_bull.json"</span>,
        <span class="hljs-string">"https://ipfs.io/ipfs/QmdcURmN1kEEtKgnbkVJJ8hrmsSWHpZvLkRgsKKoiWvW9g?filename=simple_bull.json"</span>
    ];
    <span class="hljs-keyword">string</span>[] bearUrisIpfs <span class="hljs-operator">=</span> [
        <span class="hljs-string">"https://ipfs.io/ipfs/Qmdx9Hx7FCDZGExyjLR6vYcnutUR8KhBZBnZfAPHiUommN?filename=beanie_bear.json"</span>,
        <span class="hljs-string">"https://ipfs.io/ipfs/QmTVLyTSuiKGUEmb88BgXG3qNC8YgpHZiFbjHrXKH3QHEu?filename=coolio_bear.json"</span>,
        <span class="hljs-string">"https://ipfs.io/ipfs/QmbKhBXVWmwrYsTPFYfroR2N7NAekAMxHUVg2CWks7i9qj?filename=simple_bear.json"</span>
    ];


    <span class="hljs-comment">// random</span>
    VRFCoordinatorV2Interface COORDINATOR;

    <span class="hljs-comment">// Your subscription ID.</span>
    <span class="hljs-keyword">uint64</span> s_subscriptionId;

    <span class="hljs-comment">// Goerli coordinator. For other networks,</span>
    <span class="hljs-comment">// see https://docs.chain.link/docs/vrf-contracts/#configurations</span>
    <span class="hljs-keyword">address</span> vrfCoordinator <span class="hljs-operator">=</span> <span class="hljs-number">0x6168499c0cFfCaCD319c818142124B7A15E857ab</span>;

    <span class="hljs-comment">// The gas lane to use, which specifies the maximum gas price to bump to.</span>
    <span class="hljs-comment">// For a list of available gas lanes on each network,</span>
    <span class="hljs-comment">// see https://docs.chain.link/docs/vrf-contracts/#configurations</span>
    <span class="hljs-keyword">bytes32</span> keyHash <span class="hljs-operator">=</span> <span class="hljs-number">0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc</span>;

    <span class="hljs-comment">// Depends on the number of requested values that you want sent to the</span>
    <span class="hljs-comment">// fulfillRandomWords() function. Storing each word costs about 20,000 gas,</span>
    <span class="hljs-comment">// so 100,000 is a safe default for this example contract. Test and adjust</span>
    <span class="hljs-comment">// this limit based on the network that you select, the size of the request,</span>
    <span class="hljs-comment">// and the processing of the callback request in the fulfillRandomWords()</span>
    <span class="hljs-comment">// function.</span>
    <span class="hljs-keyword">uint32</span> callbackGasLimit <span class="hljs-operator">=</span> <span class="hljs-number">100000</span>;

    <span class="hljs-comment">// The default is 3, but you can set this higher.</span>
    <span class="hljs-keyword">uint16</span> requestConfirmations <span class="hljs-operator">=</span> <span class="hljs-number">3</span>;

    <span class="hljs-comment">// For this example, retrieve 2 random values in one request.</span>
    <span class="hljs-comment">// Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.</span>
    <span class="hljs-keyword">uint32</span> numWords <span class="hljs-operator">=</span>  <span class="hljs-number">2</span>;
    <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">public</span> s_randomWords;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> s_requestId;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">TokensUpdated</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> marketTrend</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">uint</span> updateInterval, <span class="hljs-keyword">address</span> _priceFeed, <span class="hljs-keyword">uint64</span> subscriptionId</span>) <span class="hljs-title">ERC721</span>(<span class="hljs-params"><span class="hljs-string">"Bull&#x26;Bear"</span>, <span class="hljs-string">"BBTK"</span></span>) <span class="hljs-title">VRFConsumerBaseV2</span>(<span class="hljs-params">vrfCoordinator</span>) </span>{
        interval <span class="hljs-operator">=</span> updateInterval;
        lastTimeStamp <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>;

        <span class="hljs-comment">// https://rinkeby.etherscan.io/address/0xECe365B379E1dD183B20fc5f022230C044d51404</span>
        priceFeed <span class="hljs-operator">=</span> AggregatorV3Interface(_priceFeed);
        currentPrice <span class="hljs-operator">=</span> getLatestPrice();

        COORDINATOR <span class="hljs-operator">=</span> VRFCoordinatorV2Interface(vrfCoordinator);
        s_subscriptionId <span class="hljs-operator">=</span> subscriptionId;
    }

    <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>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-comment">// Current counter value will be the minted token's token ID.</span>
        <span class="hljs-keyword">uint256</span> tokenId <span class="hljs-operator">=</span> _tokenIdCounter.current();

        <span class="hljs-comment">// Increment it so next time it's correct when we call .current()</span>
        _tokenIdCounter.increment();

        <span class="hljs-comment">// Mint the token</span>
        _safeMint(to, tokenId);

        <span class="hljs-comment">// Default to a bull NFT</span>
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> defaultUri <span class="hljs-operator">=</span> bullUrisIpfs[s_randomWords[<span class="hljs-number">0</span>]<span class="hljs-operator">%</span><span class="hljs-number">3</span>];
        _setTokenURI(tokenId, defaultUri);

        console.log(
            <span class="hljs-string">"DONE!!! minted token "</span>,
            tokenId,
            <span class="hljs-string">" and assigned token url: "</span>,
            defaultUri
        );
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkUpkeep</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span> upkeepNeeded, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> <span class="hljs-comment">/*performData*/</span></span>)</span>{
        upkeepNeeded <span class="hljs-operator">=</span> (<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">-</span> lastTimeStamp) <span class="hljs-operator">></span> interval;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">performUpkeep</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span></span>{
        <span class="hljs-keyword">if</span>((<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">-</span> lastTimeStamp) <span class="hljs-operator">></span> interval){
            lastTimeStamp <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>;
            <span class="hljs-keyword">int</span> latestPrice <span class="hljs-operator">=</span> getLatestPrice();

            <span class="hljs-keyword">if</span>(latestPrice <span class="hljs-operator">=</span><span class="hljs-operator">=</span> currentPrice){
                <span class="hljs-keyword">return</span>;
            }<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(latestPrice <span class="hljs-operator">&#x3C;</span> currentPrice){
                updateAllTokenUris(<span class="hljs-string">"bears"</span>);
            }<span class="hljs-keyword">else</span>{
                updateAllTokenUris(<span class="hljs-string">"bull"</span>);
            }

            currentPrice <span class="hljs-operator">=</span> latestPrice;
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getLatestPrice</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span>(<span class="hljs-params"><span class="hljs-keyword">int256</span></span>)</span>{
        (,
        <span class="hljs-keyword">int</span> price,
        ,
        ,) <span class="hljs-operator">=</span> priceFeed.latestRoundData();
        <span class="hljs-keyword">return</span> price;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateAllTokenUris</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> trend</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span></span>{
        <span class="hljs-keyword">if</span>(compareStrings(<span class="hljs-string">"bears"</span>, trend)){
            <span class="hljs-keyword">for</span>(<span class="hljs-keyword">uint</span> i<span class="hljs-operator">=</span><span class="hljs-number">0</span>; i<span class="hljs-operator">&#x3C;</span> _tokenIdCounter.current(); i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>){
                _setTokenURI(i,bearUrisIpfs[s_randomWords[<span class="hljs-number">0</span>]<span class="hljs-operator">%</span><span class="hljs-number">3</span>]);
            }
        }<span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">for</span>(<span class="hljs-keyword">uint</span> i<span class="hljs-operator">=</span><span class="hljs-number">0</span>; i<span class="hljs-operator">&#x3C;</span> _tokenIdCounter.current(); i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>){
                _setTokenURI(i,bullUrisIpfs[s_randomWords[<span class="hljs-number">0</span>]<span class="hljs-operator">%</span><span class="hljs-number">3</span>]);
            }
        }

        <span class="hljs-keyword">emit</span> TokensUpdated(trend);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setInterval</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> newInterval</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span></span>{
        interval <span class="hljs-operator">=</span> newInterval;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setPriceFeed</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> newFeed</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span></span>{
        priceFeed <span class="hljs-operator">=</span> AggregatorV3Interface(newFeed);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">compareStrings</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> a, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> b</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>{
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(a)) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(b));
    }

    <span class="hljs-comment">// The following functions are overrides required by Solidity.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_beforeTokenTransfer</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>,
        <span class="hljs-keyword">address</span> to,
        <span class="hljs-keyword">uint256</span> tokenId
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span>(<span class="hljs-params">ERC721, ERC721Enumerable</span>) </span>{
        <span class="hljs-built_in">super</span>._beforeTokenTransfer(<span class="hljs-keyword">from</span>, to, tokenId);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_burn</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>)
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span>
        <span class="hljs-title"><span class="hljs-keyword">override</span></span>(<span class="hljs-params">ERC721, ERC721URIStorage</span>)
    </span>{
        <span class="hljs-built_in">super</span>._burn(tokenId);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tokenURI</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>)
        <span class="hljs-title"><span class="hljs-keyword">public</span></span>
        <span class="hljs-title"><span class="hljs-keyword">view</span></span>
        <span class="hljs-title"><span class="hljs-keyword">override</span></span>(<span class="hljs-params">ERC721, ERC721URIStorage</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">return</span> <span class="hljs-built_in">super</span>.tokenURI(tokenId);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">supportsInterface</span>(<span class="hljs-params"><span class="hljs-keyword">bytes4</span> interfaceId</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">override</span></span>(<span class="hljs-params">ERC721, ERC721Enumerable</span>)
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.supportsInterface(interfaceId);
    }

        <span class="hljs-comment">// Assumes the subscription is funded sufficiently.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">requestRandomWords</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-comment">// Will revert if subscription is not set and funded.</span>
        s_requestId <span class="hljs-operator">=</span> COORDINATOR.requestRandomWords(
        keyHash,
        s_subscriptionId,
        requestConfirmations,
        callbackGasLimit,
        numWords
        );
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fulfillRandomWords</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span>, <span class="hljs-comment">/* requestId */</span>
        <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> randomWords
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        s_randomWords <span class="hljs-operator">=</span> randomWords;
    }
}
</code></pre><h2 id="h-3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3.部署合约</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/de2a011f747a59ebb7143869716cf19f8251964f4669f8f2260ba0e36002cf94.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/561226db2e7b82bbbaaf6a9f1bc566de3abb51d9b46f0485990f932040dcb8f4.png" alt="订阅的id" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">订阅的id</figcaption></figure><p>订阅的id</p><p>id我们将需要部署的合约选择 Bull&amp;Bear 合约，账号选择我们刚才领取测试币的账号，部署的参数UPDATEINTERVAL填写 10，_PRICEFEED 为 Link 上面测试ILV合约的预言机地址 <code>0x48731cF7e84dc94C5f84577882c14Be11a5B7456</code>（合约地址来源<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/docs/ethereum-addresses/">https://docs.chain.link/docs/ethereum-addresses/</a>），SUBSCRIPTIONID 为你自己申请的Link预言机订阅号。等待部署完成获取合约的地址。</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><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6a9b4a237720af2521bdfec02c5dd7ac0201683cafbaf672d0b81c587aea207e.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>当添加成功后，我们在使用合约里面的函数来获取随机数。首先点击requestRandomWords函数，等待交易确认后在s_randomWords函数后输入0获取第一个数，我们可以稍微留意下现在这个数，过了一分钟，我们在重复现在做的事：首先点击requestRandomWords然后在点击s_randomWords函数后输入0。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1892d660894ff17713cceb85e8503e914147418ec21bb9698092a0c40405607b.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><h3 id="h-32mintnft" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.2mint给自己NFT</h3><p>我们在afemint里面填写自己的ETH地址，给自己mint一个NFT，等NFT mint成功以后，我们在tokenURI 这边输入0，NFT的元数据信息。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6cc82d806e1efd970d7037015fe69957d378113fd4378be956d934226069828a.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/c1761ca9d0ca7b9c453253bc950e094a82810ef435b20987a3c08f9d7ef6c76a.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>我们在setPriceFeed方法里输入Link预言机以太坊价格的合约地址 0x8A753747A1Fa494EC906cE90E9f37563A8AF630e，将把原来的LIV价格的预言机改ETH的，以点击一下 getLatestPrice方法，可以看到int256后面就是最新的以太坊价格，其中前4位是个位数，后面8位为小数，我们在performUpkeep方法的参数里输入“[]”，点击call，手动来触发价格更新方法，这一步方法里也会更新我们的NFT元数据我们再次点击获取Token Id 为0的NFT的元数据，可以看到元数据会变成这三个中的一个。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d21e99db942a8db6af5723a4bbc4258541a63baa9c98ea43a91145e10223cd95.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/">https://testnets.opensea.io/</a> 刷新元数据是否不一样了</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8b5563f16e32b842d0b5c4dd1a7c6ac00228c0ac413385912a9f7230a1ed609e.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/b594ce52443f23d80430579550a3288ec2beb4b1d2d36eb6d21c7bfa02107c5d.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>sheepswap@newsletter.paragraph.com (Robby)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3第四周文本教程-如何创建 NFT 画廊]]></title>
            <link>https://paragraph.com/@sheepswap/alchemy-the-road-to-web3-nft</link>
            <guid>ZjuBUhNVxT2tCNUNkoj9</guid>
            <pubDate>Wed, 21 Sep 2022 16:11:42 GMT</pubDate>
            <description><![CDATA[在本教程中，您将学习如何使用 Alchemy NFT API 来构建一个能够基于三件事获取 NFT 的 NFT 库：钱包地址收货地址钱包地址+收款地址前面我们一直使用的是VScode，但是后续很多人都跟帝哥说不是很会使用，所以今天在这里帝哥在给大家介绍使用一个新的网站。https://replit.com/，十分方便我们开发。 好，下面开始我们今天的课程。1.初始化项目我们是选取replit上面新建一个项目# 创建成功以后在shell里输入如下代码，其中coinmanlab为你的项目名字，不要有大写，可以随意 npx create-next-app -e with-tailwindcss coinmanlab 2.编写代码因为本教程将使用 Javascript 而不是 Typescript，所以在编写代码之前，需要将文件修改成Javascript认可的文件，参考如下：代码需要修改如下： app.jsximport '../styles/globals.css' function MyApp({ Component, pageProps }) { return &#x3C;Comp...]]></description>
            <content:encoded><![CDATA[<p>在本教程中，您将学习如何使用 Alchemy NFT API 来构建一个能够基于三件事获取 NFT 的 NFT 库：</p><ol><li><p>钱包地址</p></li><li><p>收货地址</p></li><li><p>钱包地址+收款地址</p></li></ol><p>前面我们一直使用的是VScode，但是后续很多人都跟帝哥说不是很会使用，所以今天在这里帝哥在给大家介绍使用一个新的网站。<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://replit.com/">https://replit.com/</a>，十分方便我们开发。</p><p>好，下面开始我们今天的课程。</p><h3 id="h-1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.初始化项目</h3><p>我们是选取replit上面新建一个项目</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a9b88b85edd453fa1ff684ead911d894b994a389f42a9d0e878a445a0bc37986.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="# 创建成功以后在shell里输入如下代码，其中coinmanlab为你的项目名字，不要有大写，可以随意
npx create-next-app -e with-tailwindcss coinmanlab
"><code># 创建成功以后在shell里输入如下代码，其中coinmanlab为你的项目名字，不要有大写，可以随意
npx create<span class="hljs-operator">-</span>next<span class="hljs-operator">-</span>app <span class="hljs-operator">-</span>e with<span class="hljs-operator">-</span>tailwindcss coinmanlab
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4638991b9b30b01826aea33b22c733858e3d3fdbe0b20a3be9b4011e908492e5.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-2" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.编写代码</h3><p>因为本教程将使用 Javascript 而不是 Typescript，所以在编写代码之前，需要将文件修改成Javascript认可的文件，参考如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7556a60286ad3afbe51d2e372a7c36493574d5de5090e23151d327ab1c70d3ea.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>app.jsx</p><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><p>index.jsx</p><pre data-type="codeBlock" text="import { NFTCard } from &quot;./nftCard&quot;
import { useState } from &apos;react&apos;
import Head from &apos;next/head&apos;
import Image from &apos;next/image&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;Coinman&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;1111&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} onChange={(e) =&gt; { setWalletAddress(e.target.value) }} value={wallet} type={&quot;text&quot;} placeholder=&quot;Add your wallet address&quot;&gt;&lt;/input&gt;
        &lt;input type={&quot;text&quot;} onChange={(e) =&gt; { setCollectionAddress(e.target.value) }} value={collection} 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"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Head</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'next/head'</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">Image</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'next/image'</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">"Coinman"</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">"1111"</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} 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-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><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>} onChange<span class="hljs-operator">=</span>{(e) <span class="hljs-operator">=</span><span class="hljs-operator">></span> { setCollectionAddress(e.target.<span class="hljs-built_in">value</span>) }} value<span class="hljs-operator">=</span>{collection} 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><p>同时我们需要新建一个jsx文件，nftCard.jsx，代码如下：</p><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><p>现在我们的代码编写完毕了，但是还有部分需要我们去更改的，更改如下：</p><p>index.jsx里有两个地方有const api_key = &quot;Coinman&quot;，需要把这个api_key的值修改为你的alchemy账号的ETH主网的api key，具体如图所示：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/61b50d995510758a99dab8526f326070d05dfe7bf2ecbc5f2494701b47373f07.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>另外这张图需要我们注意的：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7027ee02875cc3b78d46e1b6acbfcb82fac9b5520400fd62e74c226453e2757c.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>第一点就是我们需要修改为jsx</p><pre data-type="codeBlock" text="# 进入你的项目
cd coinmanlab
# 执行项目
npm run dev
"><code><span class="hljs-comment"># 进入你的项目</span>
<span class="hljs-built_in">cd</span> coinmanlab
<span class="hljs-comment"># 执行项目</span>
npm run dev
</code></pre><p>当我们正确执行后页面会显示出来我们的页面结果：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/99b24176c5dc2910fa66e9c2e5c638ad7a648d131edff73e8f1e219a887f8a47.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：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ad712510b8d03053e8a0444fbfddfb7ecc10b0ced807f0104beb6d72470a2e92.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>sheepswap@newsletter.paragraph.com (Robby)</author>
        </item>
        <item>
            <title><![CDATA[跟着Alchemy边学边获取NFT]]></title>
            <link>https://paragraph.com/@sheepswap/alchemy-nft</link>
            <guid>onGYzjFewr7CqS3SCdj0</guid>
            <pubDate>Wed, 14 Sep 2022 14:29:29 GMT</pubDate>
            <description><![CDATA[1.编写合约 首先我们来到合约编写页面 https://docs.openzeppelin.com/contracts/4.x/wizard2.将合约使用Remix修改和部署 导入Remix后合约的结构如下：我们需要修改如下几点，你们也可以直接将帝哥的代码覆盖你的函数即可： 1.该NFT是任何人都可以mint的，所以修改如下 function safeMint(address to, string memory uri) public { require(_tokenIdCounter.current() &#x3C;= MAX_SUPPLY, "I'm sorry we reached the cap"); uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); _setTokenURI(tokenId, uri); } 2.修改NFT的供应总量 uint256 MAX_SUPPLY = 100000; //将该代码添加在Counters....]]></description>
            <content:encoded><![CDATA[<p><strong>1.编写合约</strong></p><p>首先我们来到合约编写页面</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts/4.x/wizard">https://docs.openzeppelin.com/contracts/4.x/wizard</a></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ac66f66aa8d5f48b9dfe1926b128f40b93bf5df73d773725a9f8d41f9bd762dc.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>2.将合约使用Remix修改和部署</strong></p><p>导入Remix后合约的结构如下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3eedee623c2a3b7127206429d1bd7321315ecfc1c5cf041a5fc8c99608b183b1.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><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://rinkebyfaucet.com/">rinkebyfaucet.com</a></p><p><strong>4.创建一个Alchemy的app</strong></p><p>1.首先我们去到Alchemy注册一个账户，同时新建一个app（需要选择以太坊生态和Rikney）</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/">www.alchemy.com</a></p><p>查看我们的Key</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/df1d51e78daa703dc85fdb3d145fa9557a97c1d1475de50666854e06f5dc3cd2.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><p>部署合约</p><p>将出现一个 Metamask 弹出窗口，点击“签名”，然后继续支付 gas 费用。如果一切都按预期工作，10 秒后，应该会在 Deployed Contracts 下看到该合约：</p><p>0x027122fF7F9b1833914F513f07d2A3FB31D20eFD</p><p>已经部署完成合约</p><p><strong>7.上传我们的NFT元数据</strong></p><p>Pinata网站管理我们的元数据，我们只需要申请一个账号，会免费给我1g的存储空间。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://app.pinata.cloud/">app.pinata.cloud</a></p><p>mint我们的NFT，在safeMint输入我们的钱包地址和刚才编写的uri（ipfs://\&lt;your\_metadata\_cid&gt;）使用json的。</p><p><strong>8.查看我们的NFT</strong></p><p>我们去到OpenSea测试网站查看我们做的NFT</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnets.opensea.io/zh-CN">testnets.opensea.io</a></p><p>我们找到对应合约的NFT，现在我们看到的合约地址和我们刚才部署的合约地址是否对应上，现在还是盲盒，需要我们自己去操作下</p><p><strong>9.开盲盒</strong></p><p>在tokenUri 插入“0”作为 id 参数，点击call，它应该显示你的 tokenURI。</p>]]></content:encoded>
            <author>sheepswap@newsletter.paragraph.com (Robby)</author>
        </item>
        <item>
            <title><![CDATA[Alchemy的the Road to Web3第二周文本教程]]></title>
            <link>https://paragraph.com/@sheepswap/alchemy-the-road-to-web3</link>
            <guid>4Z8eLnWvpuvdq0aUmDkG</guid>
            <pubDate>Wed, 14 Sep 2022 12:49:10 GMT</pubDate>
            <description><![CDATA[今天我们一起来看看第二周任务。首先看看任务的目标是什么？在本教程中，将学习如何使用Alchemy、Hardhat、Ethers.js开发和部署去中心化的“给我买杯咖啡”智能合约，允许访问者发送（假）ETH 作为提示并留下好消息**。**话不多说，我们现在就开始第二周课程吧。 1.先决条件 npm (npx) version 8.5.5 node version 16.13.1 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@^5.0.0当你创建成功后目录如下所...]]></description>
            <content:encoded><![CDATA[<p>今天我们一起来看看第二周任务。首先看看任务的目标是什么？在本教程中，将学习如何使用Alchemy、Hardhat、Ethers.js开发和部署去中心化的“给我买杯咖啡”智能合约，允许访问者发送（假）ETH 作为提示并留下好消息**。**话不多说，我们现在就开始第二周课程吧。</p><p>1.先决条件</p><p>npm (npx) version 8.5.5 node version 16.13.1 2.创建项目</p><p>#创建一个项目目录并且初始化package.json mkdir BuyMeACoffee-contracts cd BuyMeACoffee-contracts npm init -y</p><h1 id="h-hardhat" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">使用hardhat生成项目框架</h1><p>npx hardhat</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">该命令建议执行，因为依赖可能有问题</h1><p>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</p><h1 id="h-" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">当你创建成功后目录如下所示：</h1><p>. ├── <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://README.md">README.md</a> ├── contracts ├── hardhat.config.js ├── node_modules ├── package-lock.json ├── package.json ├── scripts └── test</p><p>contracts- 您的智能合约所在的文件夹 在这个项目中，我们将只创建一个，来组织我们的逻辑BuyMeACoffee scripts- 您的安全帽 javscript 脚本所在的文件夹 我们将编写逻辑deploy 示例脚本buy-coffee 和一个兑现我们小费的脚本withdraw hardhat.config.js- 带有solidity版本和部署设置的配置文件 3.开始开发项目</p><p>我们可以使用任意的vim方式打开上面创建的项目，帝哥这里就用VScode了，如果有需要的可以去官网下载</p><p>Visual Studio Code - Code Editing. Redefined Visual Studio Code - Code Editing. Redefined Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications. Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://code.visualstudio.com">code.visualstudio.com</a></p><p>我们可以看到生成的目录已经有了一个合约，我们只需要去替换即可，首先将合约文件名替换为 BuyMeACoffee.sol 同时将合约内容替换成下面的。</p><p>//SPDX-License-Identifier: Unlicense</p><p>// contracts/BuyMeACoffee.sol pragma solidity ^0.8.0;</p><p>// Switch this to your own contract address once deployed, for bookkeeping!</p><p>contract BuyMeACoffee { // Event to emit when a Memo is created. event NewMemo( address indexed from, uint256 timestamp, string name, string message );</p><pre data-type="codeBlock" text="// 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">// 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><p>}</p><p>4.测试部署合约</p><p>将scripts下面的deploy.js的内容替换为下面的</p><p>const hre = require(&quot;hardhat&quot;);</p><p>// 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); }</p><p>// Logs the Ether balances for a list of addresses. async function printBalances(addresses) { let idx = 0; for (const address of addresses) { console.log(<code>Address ${idx} balance: </code>, await getBalance(address)); idx ++; } }</p><p>// Logs the memos stored on-chain from coffee purchases. async function printMemos(memos) { for (const memo of memos) { const timestamp = memo.timestamp; const tipper = <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://memo.name">memo.name</a>; const tipperAddress = memo.from; const message = memo.message; console.log(<code>At ${timestamp}, ${tipper} (${tipperAddress}) said: &quot;${message}&quot;</code>); } }</p><p>async function main() { // Get the example accounts we&apos;ll be working with. const [owner, tipper, tipper2, tipper3] = await hre.ethers.getSigners();</p><p>// We get the contract to deploy. const BuyMeACoffee = await hre.ethers.getContractFactory(&quot;BuyMeACoffee&quot;); const buyMeACoffee = await BuyMeACoffee.deploy();</p><p>// Deploy the contract. await buyMeACoffee.deployed(); console.log(&quot;BuyMeACoffee deployed to:&quot;, buyMeACoffee.address);</p><p>// Check balances before the coffee purchase. const addresses = [owner.address, tipper.address, buyMeACoffee.address]; console.log(&quot;== start ==&quot;); await printBalances(addresses);</p><p>// 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);</p><p>// Check balances after the coffee purchase. console.log(&quot;== bought coffee ==&quot;); await printBalances(addresses);</p><p>// Withdraw. await buyMeACoffee.connect(owner).withdrawTips();</p><p>// Check balances after withdrawal. console.log(&quot;== withdrawTips ==&quot;); await printBalances(addresses);</p><p>// Check out the memos. console.log(&quot;== memos ==&quot;); const memos = await buyMeACoffee.getMemos(); printMemos(memos); }</p><p>// 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); }); 当我们将上面的替换完毕后，通过命令行运行JS</p><p>npx hardhat run scripts/deploy.js 测试我们的合约，当你执行成功会有下面的现实</p><p>BuyMeACoffee deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 == 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; 5.使用 Alchemy 和 MetaMask 将 BuyMeACoffe.sol 智能合约部署到以太坊 Goerli 测试网</p><p>新建一个deploy01.js，内容如下：</p><p>// scripts/deploy01.js</p><p>const hre = require(&quot;hardhat&quot;);</p><p>async function main() { // We get the contract to deploy. const BuyMeACoffee = await hre.ethers.getContractFactory(&quot;BuyMeACoffee&quot;); const buyMeACoffee = await BuyMeACoffee.deploy();</p><p>await buyMeACoffee.deployed();</p><p>console.log(&quot;BuyMeACoffee deployed to:&quot;, buyMeACoffee.address); }</p><p>// 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); });</p><p>运行我们刚才脚本：</p><p>npx hardhat run scripts/deploy01.js 如果成功的话，你会看到下面这样的提示：</p><p>BuyMeACoffee deployed to:</p><p>0x085E27216E5098c8247378b35ec1484C92E1D285</p><p>这里需要注意哦，当我们多次运行的话，你每次都会看到完全相同的部署地址，这是因为当你运行脚本时，Hardhat 工具使用的默认设置是本地开发网络，就在您的计算机上。它快速且具有确定性，非常适合进行一些快速的健全性检查。</p><p>但是，为了实际部署到在 Internet 上运行且节点遍布世界各地的测试网络，我们需要更改我们的 Hardhat 配置文件以提供选项。</p><p>6.修改hardhat.config.js 进行配置部署</p><p>首先我们将hardhat.config.js先行修改如下：</p><p>// hardhat.config.js</p><p>require(&quot;@nomiclabs/hardhat-ethers&quot;); require(&quot;@nomiclabs/hardhat-waffle&quot;); require(&quot;dotenv&quot;).config()</p><p>// You need to export an object to set up your config // Go to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hardhat.org/config/">https://hardhat.org/config/</a> to learn more</p><p>const GOERLI_URL = process.env.GOERLI_URL; const PRIVATE_KEY = process.env.PRIVATE_KEY;</p><p>/**</p><ul><li><p>@type import(&apos;hardhat/config&apos;).HardhatUserConfig */ module.exports = { solidity: &quot;0.8.4&quot;, networks: { goerli: { url: GOERLI_URL, accounts: [PRIVATE_KEY] } } };</p></li></ul><h1 id="h-dotenv" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">安装dotenv</h1><p>npm install dotenv</p><h1 id="h-env" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">创建一个.env文件</h1><p>touch .env</p><h1 id="h-env" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">将下面内容写入env中</h1><p>GOERLI_URL=<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eth-goerli.alchemyapi.io/v2/">https://eth-goerli.alchemyapi.io/v2/</a> GOERLI_API_KEY= PRIVATE_KEY= 此外，为了获得所需的环境变量，可以使用以下资源： GOERLI_URL- 注册一个帐户炼金术，创建一个 Ethereum -&gt; Goerli 应用程序，并使用 HTTP URL GOERLI_API_KEY- 从您的同一个 Alchemy Ethereum Goerli 应用程序中，您可以获得 URL 的最后一部分，这将是您的 API KEY PRIVATE_KEY- 遵循这些来自 MetaMask 的说明导出您的私钥。 获取Goerli测试币，去下面网址获取测试币 Goerli Faucet A fast and reliable Ethereum Goerli testnet faucet for blockchain developers. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://www.goerlifaucet.com">www.goerlifaucet.com</a> 运行脚本发布到测试网络 npx hardhat run scripts/deploy01.js --network goerli 当你成功部署页面会显示： :BuyMeACoffee-contracts paul$ npx hardhat run scripts/deploy01.js --network goerli Compiled 1 Solidity file successfully BuyMeACoffee deployed to: 0xb322454bc226610Bb343Ac5BD813e1aF83A6BD03 验证下合约是否部署上测试网成功，我们去到测试网查看合约wo m z z <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://goerli.etherscan.io">goerli.etherscan.io</a> 我们可以看到合约已经部署完毕了。 7.实现一个脚本withdraw 我们在刚才的脚本下面新建一个withdraw.js的脚本，内容如下： // 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); }); 运行该命令在本地测试 npx hardhat run scripts/withdraw.js 如果没有报错你会有下面的提示： 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 8.使用 Replit 和 Ethers.js 构建前端 Buy Me A Coffee 网站 dapp 首先在Replit IDE将下面的仓库进行fork <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://replit.com">replit.com</a> 在fork之后我们会来到自己的工作台，如下所示： 将文件中写好的变量进行修改 更新输入contractAddresspages/index.js 将名称字符串更新为您自己的名字pages/index.js 确保合同 ABI 与您的合同相匹配utils/BuyMeACoffee.json 可以看到 contractAddress 变量已经填充了地址。修改成你自己部署的合约 将复制过来的仓库的Albert修改成你想要的任意名字都可以 将刚才在编译器生成的ABI复制到Replit中的utils/BuyMeACoffee.json 运行项目 运行成功!</p>]]></content:encoded>
            <author>sheepswap@newsletter.paragraph.com (Robby)</author>
        </item>
    </channel>
</rss>