# Web3 开发智能合约部署及合约验证全流程

By [Blue](https://paragraph.com/@blue-9) · 2023-12-27

---

> 这篇文章主要讲一下用`hardhat`框架开发好了一个`solidity`智能合约后，如何部署到`以太坊(ETH)`网络上，我会以`以太坊测试网(Sepolia)`来部署，和`以太坊主网`操作完全一样，其中会包含`测试网ETH币`领取，以一个`ERC20代币`的`智能合约`为例部署上线，以及上线后智能合约的验证(不验证的话在区块浏览器中看不到源码，别人不太相信你的项目)。

### 一、Solidity 智能合约开发

开发这一块如果各位小伙伴还不太懂的话，建议看一下我之前的文章: [聊一聊Web3是什么，Web3从入门到精通开发学习路线](https://mirror.xyz/0x7C2BF786255eDc86C28Da9543C8E662DFB1dF8b1/F9WyqPHOgfkSFt5K9MRGSUdHETOvVKQmhFqOBw5dEqA)。

这里我直接以一个`ERC20代币`的智能合约为例，项目开源在`github`上，[点此查看](https://github.com/junkaione/MyTestToken)

智能合约代码：`contract/MyTestToken.sol：`

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    
    contract MyTestToken is ERC20, Ownable {
        constructor(
            string memory name,
            string memory symbol,
            uint256 initialSupply
        ) ERC20(name, symbol) Ownable(msg.sender) {
            _mint(msg.sender, initialSupply);
        }
    
        function mint(address to, uint256 amount) public onlyOwner {
            _mint(to, amount);
        }
    
        function burn(address account, uint256 amount) public onlyOwner {
            _burn(account, amount);
        }
    }
    

单测代码：`test/MyTestToken.ts：`

    import { expect } from "chai";
    import { ethers } from "hardhat";
    
    describe("MyTestToken", function () {
      const useMTT = async () => {
        const MyTestToken = await ethers.getContractFactory("MyTestToken");
        const MTT = await MyTestToken.deploy("My Test Token", "MTT", 1000000);
    
        return MTT;
      };
    
      it("Deployment", async function () {
        const [owner] = await ethers.getSigners();
    
        const MTT = await useMTT();
    
        expect(await MTT.totalSupply()).to.equal(1000000);
        expect(await MTT.balanceOf(owner)).to.equal(1000000);
      });
    
      it("Transfer", async function () {
        const [owner, address1] = await ethers.getSigners();
    
        const MTT = await useMTT();
    
        await MTT.transfer(address1, 400000);
        expect(await MTT.balanceOf(owner)).to.equal(600000);
        expect(await MTT.balanceOf(address1)).to.equal(400000);
      });
    
      it("Mint", async function () {
        const [owner] = await ethers.getSigners();
    
        const MTT = await useMTT();
        await MTT.mint(owner, 200000);
        expect(await MTT.totalSupply()).to.equal(1200000);
        expect(await MTT.balanceOf(owner)).to.equal(1200000);
      });
    
      it("Burn", async function () {
        const [owner] = await ethers.getSigners();
    
        const MTT = await useMTT();
        await MTT.burn(owner, 300000);
        expect(await MTT.totalSupply()).to.equal(700000);
        expect(await MTT.balanceOf(owner)).to.equal(700000);
      });
    });
    

智能合约部署上线后就不能修改了，单测非常非常非常重要，务必保证每个功能点没问题

运行`npx hardhat test`执行单测，如下图所示，全部通过

![image.png](https://storage.googleapis.com/papyrus_images/3e9c6e76aceecedcd708f87e65d33296aa6007ab151beac677abfc59cc311483.png)

image.png

### 二、智能合约部署

我们的智能合约部署必须依赖区块链的节点上，如 [Infura](https://www.infura.io/zh) 或[Alchemy](https://www.alchemy.com/)

这里我以`Infura`为例，需要我们先上去注册一个账号

注册登录好后

1.  创建一个新的`API KEY`
    

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

image.png

1.  进入创建的`API KEY`里选择对应的部署的节点，这里我们部署到`ETH`的测试网`Sepolia`上，`Infura`也提供了一个可以领`Sepolia`测试币的水龙头，大家可以点后面这个链接领取测试币，[Sepolia水龙头](https://www.infura.io/faucet/sepolia)
    

![image.png](https://storage.googleapis.com/papyrus_images/3e5915f56099b110e046421baf99d9e1c2e45a282535d3036f9c3d7c40d1ec28.png)

image.png

1.  最后可以在激活的节点里复制刚才我们选择的`节点URL`
    

回到项目中

#### 1\. 安装`dotenv`并创建`env`文件

`npm i dotenv -D`安装支持读取`env`文件的依赖，并在项目跟目录创建`env`文件

![image.png](https://storage.googleapis.com/papyrus_images/38250b6e92c93b44b2557789bffe48f168f3f8467e3b43945083de634f6e6655.png)

image.png

`ETH_SEPOILA_URL`为刚才我们复制的节点URL

`PRIVATE_KEY`是我们需要准备一个钱包来部署我们的智能合约，这里要填钱包的`私钥`，请一定要保存好`私钥`不要泄漏，这个钱包在智能合约部署后为智能合约`owner`，只有他可以`增发代币`和`销毁代币`

#### 2\. 在`hardhat`配置文件中添加`Sepolia`网络节点

`hardhat.config.ts：`

    import { HardhatUserConfig } from "hardhat/config";
    import "@nomicfoundation/hardhat-toolbox";
    import dotenv from "dotenv";
    
    dotenv.config();
    
    const { ETH_SEPOILA_URL, PRIVATE_KEY = "" } = process.env;
    
    const config: HardhatUserConfig = {
      solidity: "0.8.20",
      networks: {
        sepolia: {
          url: ETH_SEPOILA_URL,
          accounts: [PRIVATE_KEY],
        },
      },
    };
    
    export default config;
    

就是引入我们`env`中的配置，并通过`networks`添加`sepolia`网络节点

#### 3\. 编写部署脚本

`scripts/deploy.ts：`

    import { ethers } from "hardhat";
    
    async function main() {
      const [deployer] = await ethers.getSigners();
    
      console.log("Deploying contracts with the account:", deployer.address);
    
      const MyTestToken = await ethers.getContractFactory("MyTestToken");
      const MTT = await MyTestToken.deploy("My Test Token", "MTT", 1000000);
    
      console.log("MyTestToken address:", await MTT.getAddress());
    }
    
    main().catch((error) => {
      console.error(error);
      process.exitCode = 1;
    });
    

其中在`MyTestToken.deploy()`方法中传入的三个参数就是我们智能合约构造函数所需的三个参数

#### 4\. 执行部署

执行`npx hardhat run scripts/deploy.ts --network sepolia`来部署

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

image.png

成功后可以看到执行部署的钱包地址，及部署后的智能合约地址，前端就可以根据这个智能合约地址来执行相应的操作了

#### 5\. 在区块浏览器中查看部署的智能合约

复制上面的智能合约地址，到 [区块浏览器](https://sepolia.etherscan.io/) 中，就可以搜索查看了

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

image.png

这个我们智能合约的主页，因为我们发的是`Token`代币，还有个`Token`代币主页，点击上图中间标红查看

![image.png](https://storage.googleapis.com/papyrus_images/9cd5273894fb3d6e2fcae924ddf1551060c34ed9541860f5c10a98fe574bb558.png)

image.png

点击下面的`Contract`后可以看到我们合约是一段编译过的字符，用户并不能看到智能合约的源码，下面教大家如何验证我们的智能合约让用户能够看到源码

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

image.png

### 三、智能合约验证

为了增强用户对我们智能合约的信任，我们一般会将智能合约验证开源以增强用户对我们智能合约的信任度

1.  在`ETH`主网区块链浏览器中申请`API KEY`
    

`API KEY`在主网及测试网是通用的，我们在主网申请即可

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

image.png

![image.png](https://storage.googleapis.com/papyrus_images/5ec3727259614dee842a636f941a5fd1008cb392bc12c76fa9aa45c1ee43ce52.png)

image.png

对`API KEY`取个名称创建即可，成功后复制这个`API KEY`

![image.png](https://storage.googleapis.com/papyrus_images/15b93d3a3f4856ed58b3790f9754a701d451aea91c587350af8b9fdb9e746ec6.png)

image.png

1.  在代码`env`中添加`ETHERSCAN_APIKEY`并设置刚才的`API KEY`
    

![image.png](https://storage.googleapis.com/papyrus_images/405450173e435620828b759f7498da0a0d7ba4963c2781a8a4029886f7ccfd8e.png)

image.png

1.  在`hardhat.config.ts`中添加相应配置
    

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

image.png

1.  运行命令，执行验证
    

`npx hardhat verify --network sepolia 0x634e6E8b817D5975B9808D0272D1f62DF1AdD91b "My Test Token" "MTT" 1000000`

这行命令格式为`npx hardhat verify --network 您部署的网络 智能合约地址 构造函数参数`

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

image.png

看到如下输出就验证成功了，成功后在区块链浏览器上就可以看到智能合约源码了

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

image.png

> 如此就完成了我们Web3 开发智能合约部署的全流程，从最初的开发，到编译，单元测试，部署，最后的验证合约。

---

*Originally published on [Blue](https://paragraph.com/@blue-9/web3)*
