话先说在前面, 文章只是学习交流. 虽然Telegram上玩土狗, 资金盘的一大堆, 但不代表这东西就合法合理, 写这个文章的目的, 一方面是学习交流, 另一方面就是从技术的角度告诉大家这玩意大概的原理, 希望大家保持清晰, 切勿瞎J8 FOMO. 记住, 搞土狗一定不能让你暴富, 反而可能会让你坐牢.
因为后面准备发一系列相关的文章, 也算是自己学习&总结, 会介绍目前常见的一些区块链应用的技术实现, 大部分背景、前置知识是相通的, 考虑到发币本身也比较简单, 如果单写发币可能文章篇幅太短, 所以准备顺便介绍一些前置知识, 基于上述, 这篇文章大概会分成以下几个部分:
DApp架构的介绍
会用到的工具链
发币实现&流程
土狗是怎么回事
没有耐心的可以直接拉倒下面具体开发那一章节
文章可能需要一定的前置知识, 对于没有太多基础的同学, 即使抛开文章本身我觉得这些知识是想要成为一个智能合约/ DApp 开发者所必须的 :
熟悉JavaScript及常见的工具链, 主要用来做合约的部署&简单的网页引导用户与智能合约进行交互.
区块链, 特别是ETH必须要有一定的认知, 也大概知道solidity
为了方便理解后面的工具链, 也方便技术朋友简单类比, 先简单介绍传统中心化应用与区块链去中心化应用的架构区别. 如果拿淘宝上买东西来举例的话, 大概是:
h5/Andorid/iOS(大前端) 负责引导用户与应用进行交互, 比如购买一个手机
请求从大前端发到中心化的服务器(服务器属于淘宝)上,(简化掉CDN, 网关, 负载均衡以及其他一系列乱七八糟的东西), 服务端代码 (Java / Go) 负责处理具体的业务逻辑, 更新买家订单信息, 更新卖家数据库等等.
可以看到你的逻辑&信息都是放在淘宝的服务器上的, 如果淘宝哪天说, ok你这个订单我不认, 那没了就没了, 从技术上看, 淘宝是完全有能力让你的某一笔订单(相关数据)完全消失的.
DApp 区别在于第二步, 请求从大前端并没有发送到中心化的服务器上, 而是通过提交Transaction的方式发送到了链上, 智能合约代码 (Solidity) 负责处理具体的业务逻辑, 如果数据比较大的话, 可能还会用到 ipfs(索引) + Arweave (存储). 草图:

回到发币, 根据架构图来看, 我们需要:
和用户交互的界面, 对于币而言, 主要就是币的买/卖, 去中心化交易所就是专门做这个的, 最常见的例如 uniswap, 所以我们不需要写代码.
provider, 上面图里出现了这个东西, 我还没来得及介绍, 本质是一个或者一群ETH节点, 它的作用是方便我们进行链上交互, 即: 查询链上信息, 发布交易(部署新合约, 与其他合约进行交互). 一般比较常见的会使用例如 alchemy, infura 之类的节点提供商, 当然你自己拿电脑起个节点理论上也是可以的, 但通常不会这么去做, 主要是部署成本和稳定性上的考虑.
从开发的视角来看, 我们需要:
solidity 编辑器, 我这边用vscode, 也可以用官方提供的 remix
开发&测试&部署工具链: 这里会使用 hardhat 类似的还有 Truffle
不管是用户真实使用还是我们自己测试, 我们都需要去和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等等的接口规范都进行了实现.
讲了这么多终于进入正题了, 先起个项目, 智能合约项目其实和传统前端很类似:
# 新建项目
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
来写具体的合约代码, 由于我们会依赖 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点:
contract NzhlCoins is ERC20, 表示我们继承ERC20代币标准, 因为基本的代币功能都被openzeppelin ERC20 实现了, 我们进行继承就行了._mint(msg.sender, initialSupply);这里 _mint 是 ERC20的方法, 作用是用来铸造代币, 这里我们把初始供应的全量代币都发送给合约的创建者(msg.sender). 合约创建本质上就是我们send msg/transaction, 所以我们就是msg.sender.
合约写好之后, 接下来需要简单测试和发布下, 需要改动到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 搜索这个合约地址, 你将看到类似

同样的方式依葫芦画瓢在主网上操作一次, 至此, 你的空气币就发布完成了. 当然根据你的需要, 可以修改 contracts/NzhlCoins.sol, 比如在转账中添加一些额外的逻辑, 譬如貔貅盘只允许买入, 少于XX个币不允许卖出就是这么实现的.
既然已经发币了, 顺便介绍下土狗, 比如刚才我们现在发了10亿个币, 一种玩法常见的玩法是: 去bsc链上组池子, 譬如组 0.5个BNB:10亿个币的LP池子, 这时候由于池子深度浅(只有0.5BNB), 随便多来几个韭菜放个10个BNB就能把你的空气币价格拉飞, 很容易造成币价飞涨, 当韭菜都已经FOMO以后, 这时候你(庄家)可以选择撤出流动性, 因为你占市场的大部分份额, 所以池子里绝大多数的BNB都到了你手里, 这时候韭菜兑换的币已经没有价值了, 所以才叫空气币, 当然真实情况下玩法会很复杂, 不过基本上都是这个原理做的改版&变种.
所以再次提醒大家, 土狗就是个传销游戏, 别去碰.
