# 手把手教程 ——Alchemy Week3

By [RogerZ](https://paragraph.com/@rogerz-2) · 2022-08-16

---

Alchemy 教程目录
------------

[**Week1: How to Develop an NFT Smart Contract (ERC721) with Alchemy 教程**](https://mirror.xyz/0xF4b64958cF4BB506A24cE5e8b8dB5fd5a0f70315/PWrvfMYu4x-5UVd31BUyEH7toL4XlLvzdpwSKdg_rv8)

[**Week2: How to Build “Buy Me a Coffee“ Defi dapp with Alchemy 教程**](https://mirror.xyz/0xF4b64958cF4BB506A24cE5e8b8dB5fd5a0f70315/DRy4cVDaPNBtxAD-Dxs0h3LPIUkzuo9TDahPfk10hnM)

Week3: How to Make NFTs with On-Chain Metadata - HardHat and JavaScript 教程
==========================================================================

[官方教程](https://docs.alchemy.com/docs/how-to-make-nfts-with-on-chain-metadata-hardhat-and-javascript)在此，也可以看 youtube 视频链接，不想看我啰嗦的可以移步。

[![]({{DOMAIN}}/editor/youtube/play.png)](https://www.youtube.com/watch?v=8FJvY4zXvPE)

### step0 环境配置

*   **系统环境：**
    
    Window10 X64
    
*   **编程环境：**
    
    nodeJS v16.15.0
    
    NPM v8.5.5
    
    nodeJS 安装回头写一篇教程吧，最简单的方法可以 google nvm，比去官网下载 nodeJS 的安装包好用多了。另外 windows 系统下需要配置环境变量，不明白的可以先百度。
    
*   **编程IDE：**
    
    VS Code
    
    VS code [下载官网](https://code.visualstudio.com/)，exe 的安装程序，按照提示即可
    

### step1 部署 web3 环境

1\. 新建文件夹 ChainBattles，在文件夹内启动 cmd，npm init -y初始化项目，获得文件 package.json。

2\. 安装 hardhat，在 cmd 中输入

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

根据提示依次

选择Create a basic sample project

选择默认路径

3\. 复制下列代码，安装需要的库

    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" "@nomiclabs/hardhat-etherscan@^3.0.1"
    

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

4\. 安装 dotenv 库

5\. 安装 OpenZeppelin

    npm install @openzeppelin/contracts
    

可以看下文件夹内的各文件，如果和以下树形图一致，那就表示环境部署没有问题了。

    .
    ├── README.md 
    ├── contracts 
    ├── hardhat.config.js 
    ├── node_modules 
    ├── package-lock.json 
    ├── package.json
    ├── scripts 
    └── test
    

### 接下来的step2，step4 需使用 VScode 打开文件夹进行编辑。

### step2 编写智能合约

在 ./contracts/ 文件夹下新建 ChainBattles.sol，复制以下代码，然后保存。

    // 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 view 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 view 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));
            require(ownerOf(tokenId) == msg.sender, "You must own this NFT to train it!");
            uint256 currentLevel = tokenIdToLevels[tokenId];
            tokenIdToLevels[tokenId] = currentLevel + 1;
            _setTokenURI(tokenId, getTokenURI(tokenId));
        }
    }
    

### step3 申请 Alchemy 帐号并配置小狐狸钱包

1\. 注册 alchemy 帐号详细步骤可以回看 week1 教程的 step3。

[https://www.alchemy.com/](https://www.alchemy.com/)

2\. 进入 Dashboard，点击 create app。

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

3\. 依次填入 Name, Description，Chain 选择 Polygon，Network 选择 Polygon Mumbai，然后点击 CREATE APP。

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

4\. 点击 VIEW KEY，复制 HTTPS 和 API KEY 的内容

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

5\. 查看小狐狸钱包中是否有 Mumbai 测试网，如果没有可以去 [chainlink.org](http://chainlink.org) 上添加一个，并保存私钥，第六步要用到。

6\. 因为交互需要消耗 gas，所以还需通过 chainlink faucet 获取测试代币 MATIC。通过 faucet 的链接，进入申请代币即可。

[https://faucets.chain.link/](https://faucets.chain.link/)

### step4 在 Mumbai 测试网络部署合约

1\. 在 ./scripts/ 文件夹下新建 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();
    

2\. 编辑 ./hardhat.config.js，复制以下代码，然后保存。

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

3\. 新建 ./.env 文件，复制以下代码，然后保存。

    MUMBAI_URL=输入你的MUMBAI_URL
    POLYSCAN_API_KEY=输入你的POLYSCAN_API_KEY
    PRIVATE_KEY=输入你的私钥
    

4\. 在 cmd 中运行 `npx hardhat run scripts/deploy.js --network mumbai`，返回

    Contract deployed to: 合约地址
    

至此，合约已经部署到了 Mumbai 测试网，还可以在浏览器中查看。

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

但是合约没有开源，还没有 verify。

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

5\. 在 cmd 中运行 `npx hardhat verify --network mumbai [合约地址]`，返回

    Nothing to compile
    Successfully submitted source code for contract
    contracts/ChainBattles.sol:ChainBattles at 合约地址 
    for verification on the block explorer. Waiting for verification result...
    
    Successfully verified contract ChainBattles on Etherscan.
    https://mumbai.polygonscan.com/address/合约地址#code
    

看到 successfully 即 verify 成功，再回到 mumbai polygonscan 上看，已经有绿勾了。

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

### step5 通过合约Mint NFT

1\. 在合约页面中，选择 Contract —> Write Contract —> Connect to Web3，在弹出的 Metamask 窗口点击下一步，最后连接。

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

2\. 在 mint 方法下，点击 write 并在弹出的 Metamask 窗口内确认。

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

3\. 在 testnet.opensea.io 中可以查看 NFT 已经在钱包中。

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

### step6 通过合约train NFT

这是一个动态的 NFT，回到合约页面中，找到 train 方法，输入自己所持有 NFT 的 tokenId（第一个输入1），再点击 write 并在弹出的 Metamask 窗口内确认。

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

在 testnet.opensea.io 中可以查看 NFT ，点击 refresh metadata 按钮后，再刷新页面，Levels 变成了 1。

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

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

### step7 在官网提交任务并 claim NFT

在官方申请网址中提交对应的信息。

[https://alchemyapi.typeform.com/roadtoweekthree](https://alchemyapi.typeform.com/roadtoweekthree)

在 mintkudos 网址中 claim Alchemy 的 NFT，通常需要一天至一周可以获得 allowlist 资格。

[https://mintkudos.xyz/claim/674](https://mintkudos.xyz/claim/674)

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

---

*Originally published on [RogerZ](https://paragraph.com/@rogerz-2/alchemy-week3)*
