# Web3 101：Dapp原理简介&实现自己的狗狗币

By [nzhl](https://paragraph.com/@nzhl) · 2022-02-13

---

前言 & 背景
-------

**话先说在前面, 文章只是学习交流. 虽然Telegram上玩土狗, 资金盘的一大堆, 但不代表这东西就合法合理, 写这个文章的目的, 一方面是学习交流, 另一方面就是从技术的角度告诉大家这玩意大概的原理, 希望大家保持清晰, 切勿瞎J8 FOMO. 记住, 搞土狗一定不能让你暴富, 反而可能会让你坐牢.**

因为后面准备发一系列相关的文章, 也算是自己学习&总结, 会介绍目前常见的一些区块链应用的技术实现, 大部分背景、前置知识是相通的, 考虑到发币本身也比较简单, 如果单写发币可能文章篇幅太短, 所以准备顺便介绍一些前置知识, 基于上述, 这篇文章大概会分成以下几个部分:

1.  DApp架构的介绍
    
2.  会用到的工具链
    
3.  发币实现&流程
    
4.  土狗是怎么回事
    

没有耐心的可以直接拉倒下面具体开发那一章节

要求
==

文章可能需要一定的前置知识, 对于没有太多基础的同学, 即使抛开文章本身我觉得这些知识是想要成为一个智能合约/ DApp 开发者所必须的 :

1.  熟悉JavaScript及常见的工具链, 主要用来做合约的部署&简单的网页引导用户与智能合约进行交互.
    
2.  区块链, 特别是ETH必须要有一定的认知, 也大概知道solidity
    

DApp架构
======

为了方便理解后面的工具链, 也方便技术朋友简单类比, 先简单介绍传统中心化应用与区块链去中心化应用的架构区别. 如果拿淘宝上买东西来举例的话, 大概是:

1.  h5/Andorid/iOS(大前端) 负责引导用户与应用进行交互, 比如购买一个手机
    
2.  请求从大前端发到中心化的服务器(服务器属于淘宝)上，(简化掉CDN, 网关, 负载均衡以及其他一系列乱七八糟的东西), 服务端代码 (Java / Go) 负责处理具体的业务逻辑, 更新买家订单信息, 更新卖家数据库等等.
    

可以看到你的逻辑&信息都是放在淘宝的服务器上的, 如果淘宝哪天说, ok你这个订单我不认, 那没了就没了, 从技术上看, 淘宝是完全有能力让你的某一笔订单(相关数据)完全消失的.

DApp 区别在于第二步, 请求从大前端并没有发送到中心化的服务器上, 而是通过提交Transaction的方式发送到了链上, 智能合约代码 (Solidity) 负责处理具体的业务逻辑, 如果数据比较大的话, 可能还会用到 ipfs(索引) + Arweave (存储). 草图:

![CApp vs DApp](https://storage.googleapis.com/papyrus_images/dfdccbf4b051212d7b1c0bb520891a80f09585e4c1490fc07e704b4ad223cf3a.png)

CApp vs DApp

工具链
===

回到发币, 根据架构图来看, 我们需要:

1.  和用户交互的界面, 对于币而言, 主要就是币的买/卖, 去中心化交易所就是专门做这个的, 最常见的例如 uniswap, 所以我们不需要写代码.
    
2.  provider, 上面图里出现了这个东西, 我还没来得及介绍, 本质是一个或者一群ETH节点, 它的作用是方便我们进行链上交互, 即: 查询链上信息, 发布交易(部署新合约, 与其他合约进行交互). 一般比较常见的会使用例如 alchemy, infura 之类的节点提供商, 当然你自己拿电脑起个节点理论上也是可以的, 但通常不会这么去做, 主要是部署成本和稳定性上的考虑.
    

从开发的视角来看, 我们需要:

1.  solidity 编辑器, 我这边用vscode, 也可以用官方提供的 remix
    
2.  开发&测试&部署工具链: 这里会使用 hardhat 类似的还有 Truffle
    
3.  不管是用户真实使用还是我们自己测试, 我们都需要去和ETH进行交互(创建合约, 发币, 转账等等), 操作合约不可能是连上某个节点然后手动去操作, 都是编写代码进行操作的, js, python都有相关的工具库支持操作合约, 这里还是使用 js, 会用到一个叫 ethers.js 的库, 类似的还有web3.js.
    
    这里的原理很简单: 你用js代码描述你需要如何与链上的合约进行交互, 由 ethers.js 负责编码你的合约完成交互(交易), 然后发送给 provider/链上节点. 所以通常来讲你需要要在 ethers.js 中声明你的 provider, 譬如提供你创建的 infura 节点的秘钥或者链接到你自己部署的ETH节点以及signer, 也就是你的私钥或是助记词.所以其实一些钱包应用譬如Metamask, 它本质也是一个provider + signer, 市面上常见的DApp让你连接Metamask然后完成各种操作, 这里Metamask 的作用就是使用你的秘钥对交易进行加密编码然后通过 Metamask 自己背后的 provider 服务发送到ETH上.
    

考虑具体需求, 我们是要发空气币, 空气币一般遵循 ERC20 的代币接口规范, 里面定义了代币基本的一些交互功能(包括转账, 余额查询, 还有代币铸造与销毁), 这种代码不需要自己写, 我们直接引用 openzeppelin, 一个高质量的智能合约工具库, 里面对常见的一些代币、NFT等等的接口规范都进行了实现.

具体开发
====

### 1\. 初始化项目

讲了这么多终于进入正题了, 先起个项目, 智能合约项目其实和传统前端很类似:

    # 新建项目
    mkdir nzhl-coins
    cd nzhl-coins
    npm init -y
    
    # 安装 hardhat
    npm i -D hardhat
    
    # 使用 hardhat 初始化项目
    npx hardhat 
    # ✔ What do you want to do? · Create an advanced sample project 
    that uses TypeScript
    # 这里选了TS模板, 然后后面的选择一直yes就行
    
    
    # vscode 打开
    code . 
    

先来简单介绍下目录

    ├── README.md  
    ├── contracts   (智能合约i.e solidity代码放这里)
    │   └── Greeter.sol
    ├── hardhat.config.ts (负责hardhat的配置)
    ├── node_modules (譬如依赖的三方工具，类库的代码都在这)
    ├── package.json 
    ├── pnpm-lock.yaml 
    ├── scripts (目前只存放了用于合约部署的脚本)
    │   └── deploy.ts
    ├── test (合约测试脚本, 本次用不到)
    │   └── index.ts
    └── tsconfig.json
    

### 2\. 编写智能合约代码

来写具体的合约代码, 由于我们会依赖 openzeppelin 的智能合约代码, 所以需要安装@openzeppelin/contracts 依赖

    npm i @openzeppelin/contracts
    

删除掉 contracts/Greeter.sol 新建 contracts/NzhlCoins.sol, 内容如下

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.4;
    
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    
    contract NzhlCoins is ERC20 {
        constructor(uint256 initialSupply) ERC20("Nzhl Coins", "nzhl") {
            _mint(msg.sender, initialSupply);
        }
    }
    

大概意思就是我们声明了一种名字叫 Nzhl Coins 的代币, 代币符号是 nzhl (类比比特币叫 BitCoin, 代币符号btc), 这里有2点:

1.  `contract NzhlCoins is ERC20` , 表示我们继承ERC20代币标准, 因为基本的代币功能都被openzeppelin ERC20 实现了, 我们进行继承就行了.
    
2.  `_mint(msg.sender, initialSupply);` 这里 \_mint 是 ERC20的方法, 作用是用来铸造代币, 这里我们把初始供应的全量代币都发送给合约的创建者(msg.sender). 合约创建本质上就是我们send msg/transaction, 所以我们就是msg.sender.
    

### 3\. 测试&部署

合约写好之后, 接下来需要简单测试和发布下, 需要改动到2个文件

**hardhat.config.ts**

主要是是声明我们的本地/测试网/主网, 一般的开发流程是本地先开发, 开发的差不多了上测试网进行测试, 最后主网上线. 我们这里用BSC链(和ETH链一样, 只不过是币安主导的, 所以测试网、主网可以直接使用币安提供的provider接口, 即你在代码中看的那个url)

    import { HardhatUserConfig } from "hardhat/config";
    import "@nomiclabs/hardhat-ethers";
    import "@typechain/hardhat";
    
    // You need to export an object to set up your config
    // Go to https://hardhat.org/config/ to learn more
    
    const config: HardhatUserConfig = {
      solidity: "0.8.4",
      networks: {
        localhost: {
          url: "http://127.0.0.1:8545",
        },
        hardhat: {},
        testnet: {
          url: "https://data-seed-prebsc-1-s1.binance.org:8545",
          chainId: 97,
          gasPrice: 20000000000,
          accounts: [
            "负责部署的钱包私钥, 一般格式是0x3432..., 这个就是上面提到的msg.sender, 后面币会转到这个钱包上",
          ],
        },
        mainnet: {
          url: "https://bsc-dataseed.binance.org/",
          chainId: 56,
          gasPrice: 20000000000,
          accounts: [
            "负责部署的钱包私钥, 一般格式是0x3432..., 这个就是上面提到的msg.sender, 后面币会转到这个钱包上",
          ],
        },
      },
    };
    
    export default config;
    

这里没什么要注意的, 把 accounts 里面换成自己的秘钥就行,

**scripts/deploy.ts**

改成如下, 大概意思就是部署 NzhlCoins 合约, 然后发行数量是10亿.

    import { ethers } from "hardhat";
    
    const utils = ethers.utils;
    
    async function main() {
      // We get the contract to deploy
      const factory = await ethers.getContractFactory("NzhlCoins");
      const contract = await factory.deploy(utils.parseEther("1000000000"));
    
      await contract.deployed();
    
      console.log("Contract deployed to:", contract.address);
    }
    
    // We recommend this pattern to be able to use async/await everywhere
    // and properly handle errors.
    main().catch((error) => {
      console.error(error);
      process.exitCode = 1;
    });
    

配置好接下来就是正式

    # 用hardhat本地起一个测试Node
    npx hardhat node
    
    # 在本地先尝试部署一下
    npx hardhat run --network localhost scripts/deploy.ts
    
    # 没问题的话, 执行部署代码把合约部署到bsc测试网
    # 这里要求你提供的秘钥对应的地址在测试网中有一定bnb, 没有的话可以
    # google 搜索 bsc testnet faucet 找水龙头领取
    npx hardhat run --network testnet scripts/deploy.ts
    
    # 如果没有意外, 你将看到
    Contract deployed to: 0x5FbDB23156...(这就是你的合约地址)
    

接下来打开 testnet.bscscan.com 搜索这个合约地址, 你将看到类似

![testnet.bscscan.com](https://storage.googleapis.com/papyrus_images/b79f757c22c7e851cacc457926ccf2fd3b91735cb7ce4326a65b21a867cf6a66.png)

testnet.bscscan.com

同样的方式依葫芦画瓢在主网上操作一次, 至此, 你的空气币就发布完成了. 当然根据你的需要, 可以修改 contracts/NzhlCoins.sol, 比如在转账中添加一些额外的逻辑, 譬如貔貅盘只允许买入, 少于XX个币不允许卖出就是这么实现的.

4\. 土狗是怎么回事
-----------

既然已经发币了, 顺便介绍下土狗, 比如刚才我们现在发了10亿个币, 一种玩法常见的玩法是: 去bsc链上组池子, 譬如组 0.5个BNB:10亿个币的LP池子, 这时候由于池子深度浅(只有0.5BNB), 随便多来几个韭菜放个10个BNB就能把你的空气币价格拉飞, 很容易造成币价飞涨, 当韭菜都已经FOMO以后, 这时候你(庄家)可以选择撤出流动性, 因为你占市场的大部分份额, 所以池子里绝大多数的BNB都到了你手里, 这时候韭菜兑换的币已经没有价值了, 所以才叫空气币, 当然真实情况下玩法会很复杂, 不过基本上都是这个原理做的改版&变种.

所以再次提醒大家, 土狗就是个传销游戏, 别去碰.

---

*Originally published on [nzhl](https://paragraph.com/@nzhl/web3-101-dapp)*
