# Alchemy第三周任务-使用Polygon链上元数据制作 NFT

By [KnowYourself](https://paragraph.com/@knowyourself-2) · 2022-10-14

---

我们都知道创建 NFT 时，最好**将元数据存储在集中式对象存储**或**IPFS**等分散式解决方案中，以避免直接在链上存储大量数据（如图像和 JSON 对象）产生的巨额 Gas 费用。

**但这有一个问题：**

不将元数据存储在区块链上将使您无法通过智能合约与之交互，因为区块无法与“外部世界”通信。\*\*如果我们想直接从我们的智能合约更新我们的元数据，我们需要将其存储在链上，但是汽油费呢？幸运的是，像 Polygon 这样的 L2 链可以提供帮助，**大大降低了 Gas 成本**，并引入了许多优势，使开发人员能够扩展其应用程序的功能。

在本教程中，学习如何**创建区块链游戏的基础知识**，开发一个完全动态的 NFT，其链上元数据会根据您与它的交互而变化，并将其部署在**Polygon Mumbai**上以降低汽油费。

**更准确地说，您将学习：**

*   如何在链上存储 NFT 元数据
    
*   什么是 Polygon 以及为什么降低 Gas 费用很重要。
    
*   如何在 Polygon Mumbai 上部署
    
*   如何处理和存储链上 SVG 图像和 JSON 对象
    
*   如何根据您与 NFT 的交互来修改元数据
    

1.Polygon PoS - 更低的 Gas 费用和更快的交易
--------------------------------

Polygon 是一个去**中心化的 EVM 兼容扩展平台**，使开发人员能够在不牺牲安全性的情况下以低交易费用构建可扩展的用户友好型 DApp。

它属于被描述为\*\*第 2 层链 (L2)\*\*的一组链，这意味着它建立在以太坊之上，以解决一些表征它的问题 - 同时依赖它来运行。

众所周知，以太坊既不快也不便宜，在其上部署智能合约可能会迅速变得非常昂贵，这就是**Polygon**或**Optimism**等 L2 解决方案发挥作用的地方。

例如，多边形有两个主要优点：

*   **更快的交易**（65,000 tx/秒 vs ~14）
    
*   每笔交易的**gas 成本比以太坊低**约 10,000 倍
    

第二个正是我们在 Polygon 上部署带有链上元数据的 NFT 智能合约的原因。一方面，如果在以太坊上存储我们的元数据时，我们可以预期每笔交易花费数百美元，**那么在 Polygon 上它的成本不会超过几美分。**

### 1.1将 Polygon Mumbai 添加到您的 Metamask 钱包

首先，让我们**将 Polygon Mumbai 添加到我们的 Metamask 钱包中。**

导航[测试polygon](https://mumbai.polygonscan.com/)网络并向下滚动到页面底部。您将看到\*\*“添加多边形网络”按钮\*\*，单击它并确认您要将其添加到 Metamask：

![](https://storage.googleapis.com/papyrus_images/4d6a86a82be826c13f928c8a53ed5dcc20c0807af1f8ceccc81bea3bb4289628.png)

### 1.2获取免费的 Matic 以部署NFT 智能合约

我们去到[mumbaifaucet.com](https://mumbaifaucet.com/)获取测试币，输入我们的地址获取测试币即可

![](https://storage.googleapis.com/papyrus_images/09a872a0acd534ce821162a069ca405cb32ce86abcb7b1f8f2b137c6a9df7d57.png)

10-20 秒后，会看到 MATIC 出现在 Metamask 钱包中。

### 2.1项目设置

    # 新建一个文件夹用于此次项目的搭建
    mkdir ChainBattled
    cd ChainBattled
    # 安装yarn并查看版本
    npm install -g yarn
    yarn --version
    yarn add hardhat
    # 初始化项目
    npx hardhat init
    

![](https://storage.googleapis.com/papyrus_images/e59b95d646a9b570c125a057abb35e3f6dfb6ee4dac2736627be3af38d0fd372.png)

现在我们需要安装**OpenZeppelin**包来访问[ERC721 智能合约](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721)我们将使用该标准作为模板来构建我们的 NFT 智能合约。

    yarn add @openzeppelin/contracts
    

### 2.2修改 hardhat.config.js 文件

将我们的hardhat.config.js 文件修改如下：

    require("dotenv").config();
    require("@nomiclabs/hardhat-waffle");
    require("@nomiclabs/hardhat-etherscan");
    
    module.exports = {
      solidity: "0.8.10",
      networks: {
        mumbai: {
          url: process.env.TESTNET_RPC,
          accounts: [process.env.PRIVATE_KEY]
        },
      },
      etherscan: {
        apiKey: process.env.POLYGONSCAN_API_KEY
      }
    };
    

### 2.3开发智能合约

在 contracts 文件夹中，创建一个新文件并将其命名为“ChainBattles.sol”。

与往常一样，我们需要指定**SPDX-Licence-Identifier**、**pragma ，并从OpenZeppelin**导入几个库，我们将用作智能合约的基础，合约内容如下：

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
    import "@openzeppelin/contracts/utils/Counters.sol";
    import "@openzeppelin/contracts/utils/Strings.sol";
    import "@openzeppelin/contracts/utils/Base64.sol";
    
    contract ChainBattles is ERC721URIStorage  {
        using Strings for uint256;
        using Counters for Counters.Counter;
        Counters.Counter private _tokenIds;
    
        mapping(uint256 => uint256) public tokenIdToLevels;
    
        constructor() ERC721 ("Chain Battles", "CBTLS"){
        }
    
        function generateCharacter(uint256 tokenId) public returns(string memory){
    
        bytes memory svg = abi.encodePacked(
            '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350">',
            '<style>.base { fill: white; font-family: serif; font-size: 14px; }</style>',
            '<rect width="100%" height="100%" fill="black" />',
            '<text x="50%" y="40%" class="base" dominant-baseline="middle" text-anchor="middle">',"Warrior",'</text>',
            '<text x="50%" y="50%" class="base" dominant-baseline="middle" text-anchor="middle">', "Levels: ",getLevels(tokenId),'</text>',
            '</svg>'
        );
        return string(
            abi.encodePacked(
                "data:image/svg+xml;base64,",
                Base64.encode(svg)
            )    
        );
      }
    function getLevels(uint256 tokenId) public view returns (string memory) {
        uint256 levels = tokenIdToLevels[tokenId];
        return levels.toString();
    }
     function getTokenURI(uint256 tokenId) public returns (string memory){
        bytes memory dataURI = abi.encodePacked(
            '{',
                '"name": "Chain Battles #', tokenId.toString(), '",',
                '"description": "Battles on chain",',
                '"image": "', generateCharacter(tokenId), '"',
            '}'
        );
        return string(
            abi.encodePacked(
                "data:application/json;base64,",
                Base64.encode(dataURI)
            )
        );
    }
    function mint() public {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();
        _safeMint(msg.sender, newItemId);
        tokenIdToLevels[newItemId] = 0;
        _setTokenURI(newItemId, getTokenURI(newItemId));
    }
    
    function train(uint256 tokenId) public{
      require(_exists(tokenId),"The tokenId does not exist");
      require(_isApprovedOrOwner(msg.sender, tokenId),"You are not the owner of the NFT"); 
      tokenIdToLevels[tokenId] += 1;
      _setTokenURI(tokenId, getTokenURI(tokenId));
    }
    }
    

我们在上面将合约全部完善了，也新增了 4 个不同的功能：

*   \*\*generateCharacter：\*\*生成和更新我们 NFT 的 SVG 图像
    
*   \*\*getLevels：\*\*获取 NFT 的当前级别
    
*   \*\*getTokenURI ：\*\*获取 NFT 的 TokenURI
    
*   \*\*mint:\*\*到 mint - 当然
    
*   \*\*train：\*\*训练 NFT 并提高其等级
    

首先，让我们在项目的根文件夹中新建一个 .env 文件，并添加以下变量：

    TESTNET_RPC=""
    PRIVATE_KEY=""
    POLYGONSCAN_API_KEY=""
    

然后，导航到[alchemy.com](https://www.alchemy.com/)并创建一个新的 Polygon Mumbai 应用程序：

单击新创建的应用程序，复制 API HTTP URL，并将 API 作为“ **TESTNET\_RPC** ”值粘贴到我们在上面创建的 .env 文件中。

**打开您的Metamask**钱包，点击三个点菜单 > 帐户详细信息 > 并将您的私钥复制粘贴为.`.env`

最后，继续[Polygon,](https://polygonscan.com/)并创建一个新帐户，登录后，进入**个人资料菜单**并单击 API Keys，如果没有则需要新建一个：

![](https://storage.googleapis.com/papyrus_images/a118cd74182eec056389e210a83c632d1928e5aee373c2be5724d133bc39cbb1.png)

现在将 Api-Key 令牌复制粘贴为 .env 中的“ **POLYGONSCAN _API\_KEY_** _”值。最终结果如下：_

![](https://storage.googleapis.com/papyrus_images/e7f56513399c6ee9ac7cbaa8372c0b53a7c368cc41df923a4d1f4e0bc0f16966.png)

_在部署我们的智能合约之前的最后一步，我们需要_**_创建部署脚本。_**

### 3.1创建部署脚本

首先安装依赖

    npm install dotenv
    npm install @nomiclabs/hardhat-waffle
    

我们将deploy.js的脚本替换如下：

    const main = async () => {
      try {
        const nftContractFactory = await hre.ethers.getContractFactory(
          "ChainBattles"
        );
        const nftContract = await nftContractFactory.deploy();
        await nftContract.deployed();
    
        console.log("Contract deployed to:", nftContract.address);
        process.exit(0);
      } catch (error) {
        console.log(error);
        process.exit(1);
      }
    };
      
    main();
    

### 3.2编译和部署智能合约

当我们把脚本写好后，编译智能合约，只需在项目内的终端中运行以下命令：

    npx hardhat compile
    

如果一切按预期进行，你将**在 artifacts 文件夹中看到已编译的智能合约。**

现在，让我们在运行的 Polygon Mumbai 链上部署智能合约

    npx hardhat run scripts/deploy.js --network mumbai
    

如果一切正常你将在页面看到合约的地址

![](https://storage.googleapis.com/papyrus_images/3546623bdc3d646e945ef92327471176328142550136312b95c843ead704b590.png)

### 3.3在 Polygon Scan 上检查智能合约

复制刚刚部署的智能合约的地址，去[测试polygon](https://mumbai.polygonscan.com/)网络，然后\*\*在搜索栏中粘贴智能合约的地址。\*\*进入智能合约页面后，单击“合约”选项卡。你会注意到合约代码不可读：

![](https://storage.googleapis.com/papyrus_images/f1d9c2f9c207165fca1b8cacb96aef3950ebc0b273f97c15d1e8f2e82ff2ff33.png)

**这是因为我们还没有验证我们的代码。**

为了验证我们的智能合约，我们需要回到我们的项目，并在终端中运行以下代码：

    # npx hardhat verify --network mumbai 合约地址
    npx hardhat verify --network mumbai 0x99299c6B27eBB1B6dAf4BD3016CBd58D9656A37d
    

报错信息：

    Error in plugin @nomiclabs/hardhat-etherscan: Failed to send contract verification request.
    Endpoint URL: https://api-testnet.polygonscan.com/api
    Reason: read ECONNRESET
    

通过网站添加方法，点击立即验证即可：

![](https://storage.googleapis.com/papyrus_images/29dcf0439b0ac777b0dee89ef861b83216148686631111f7bd32f1a0ee6963e4.png)

### 3.4通过多边形扫描与您的智能合约交互

现在智能合约已经通过验证，mumbai.polygonscan.com 将在其附近显示一个绿色小勾：

![](https://storage.googleapis.com/papyrus_images/89754d9ad2470acfb2068704d3e57e9ea13044aa2777ef3a39439a8114d370d9.png)

![](https://storage.googleapis.com/papyrus_images/0486c8cd07473c45873a2127267463d700f455a70b1b5a45b8cf40f2a21707e1.png)

然后寻找“min​​t”函数并点击Write：

![](https://storage.googleapis.com/papyrus_images/32088f7a1758ebee67a4d30230b9febeadfdfae6ba1d3f1622b4d524a23078e0.png)

这将打开一个 Metamask 弹出窗口，要求支付 gas 费用，单击签名按钮。恭喜！您刚刚铸造了您的第一个动态 NFT - 让我们转移到 OpenSea 测试网来现场查看它。

4.在 OpenSea 上查看您的动态 NFT
-----------------------

复制智能合约地址，前往[testnet.opensea.com](https://testnets.opensea.io/)，并将其粘贴到搜索栏中：

![](https://storage.googleapis.com/papyrus_images/fcf79355b7791298b9b379ffb4e32aadb5b2f98f1f0b7fc4c21a78ac676017e7.png)

如果一切正常，现在应该会看到您的 NFT 显示在 OpenSea 上，其中包含动态图像、标题和描述。

### 4.1更新动态 NFT 图像训练 NFT

导航回[测试.polygons](https://mumbai.polygonscan.com/)网络，**点击合约标签 > 写合约**并寻找“train”功能。

插入您的 NFT 的 ID - 在这种情况下为“1”，因为我们只铸造了一个，然后**单击写入：**

![](https://storage.googleapis.com/papyrus_images/89efa6480a35a5a41cfa128a9b2e1becd766571fc27db3c95c20016cbedaaf65.png)

然后回到[testnets.opensea.com](https://testnets.opensea.io/)并刷新页面：

![](https://storage.googleapis.com/papyrus_images/7747f17de4d3c50ccec549179f471c89c5e1f6bcb9659bc40ea9e27297158946.png)

---

*Originally published on [KnowYourself](https://paragraph.com/@knowyourself-2/alchemy-polygon-nft)*
