Web3带着大家根据ERC-20文档编写自己的第一个代表solidity智能合约

上文 Web3代币基本token概念 我们简单讲述了代币的概念
也讲到了ERC-20这个协议的概念
ERC-20 官方文档地址如下 https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
说实话 给人的感觉不是很正规 连地址都是放在github上的 不过也没办法 官方都这么弄 我们也只能这么看了

先进入文档
在这里插入图片描述
翻一翻文档 你会发现 其实也不是特别多

下面会告诉我们 需要一个name方法
在这里插入图片描述
用view标注 表示他要读取状态变量 returns 返回的 是个字符串类型的值
这个name 就是我们自己代币的名字

好 那就别光说不练啦 先将ganache环境启起来
在这里插入图片描述
然后打开一个 Truffle 项目环境
在这里插入图片描述
在 contracts 目录下创建一个文件 叫 grToken.sol

参考代码如下

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract grToken{
    
    
    //因为绑定了public  会自动生成get方法
    string public name = "gerToken";

}

这样 就生成了一个叫 gerToken 的代币
甚至 就这样 就已经可以测试了

我们 在 migrations 目录下创建一个文件
还记得吧 migrations 下的文件 必须用数字开头 才能读到这个脚本
我们创建一个文件 叫 2_contract.js
参考代码如下

const Contacts = artifacts.require("grToken.sol")
module.exports = function(deployer) {
    
    
    deployer.deploy(Contacts)
}

我们导入了contracts下的grToken
然后 我们终端运行 这个项目
在这里插入图片描述
然后我们执行

truffle migrate

将合约部署上去
在这里插入图片描述
这里有人会担心 truffle migrate之后 会不会将我们之前的文件都部署上去 其实是不用担心的
因为 truffle migrate 还是有点智能的 但不多
你已经部署上去的文件 他就不会再部署一次了
但是 如果你讲内容改了 再去truffle migrate 它一样不会重新部署
所以 如果你希望改的内容重新部署上去 你需要

truffle migrate --reser

这样 你改完的文件 才会重新部署到我们的区块链上

然后 我们终端运行

truffle console

运行到truffle控制台

然后 我们执行

const token = await grToken.deployed()

在这里插入图片描述
这样 常量token就拿到了我们grToken合约对象
然后 我们尝试调用它的name

token.name

在这里插入图片描述
输出之后 会发现 这是个函数

这就算我们实现的第一个这个文档的name功能了在这里插入图片描述
然后 文档中 name后面有个 symbol
这个你可以理解为 我们之前是 ETH 你也可以给自己的token起个标识
在这里插入图片描述
我们直接加一行就好了
和name都是一样的

string public symbol = "GRY";

在这里插入图片描述
我这里的就叫 GRY 代理符号

然后 下面还有一个 decimals
在这里插入图片描述
之前我们讲过 这里 我们为了方便处理 有很多的单位 其中最基本的是 wei
在这里插入图片描述
就是你代币的换算单位
我们这里 可以这样写

uint public decimals = 18;

在这里插入图片描述
下面还有 totalSupply 啊 这完了哈
在这里插入图片描述
共用的总量
我们可以这样写

uint public totalSupply;
constructor(){
    
    
   totalSupply = 1000000 *(19 * decimals);
}

在这里插入图片描述

这里 反正我们有多少 自己说了算 直接一百万走起啊 总量 然后 的话 因为是 wei单位的 后面我们就处理一下计算 把他真的变成和 ETH一样的单位
总量 一百万 十乘以十的十八次方

然后 我们重新运行终端 输入

truffle migrate --reser

在这里插入图片描述
因为之前部署过 grToken.sol 区块链上已经记录了 所以为了修改的内容能够上去 我们需要用–reser
然后 我们执行

truffle console

打开 truffle 控制台
然后 还是

const token = await grToken.deployed()

在这里插入图片描述
再次创建一个 token对象 接这个 grToken对象

然后 我们可以 通过

token.name()

在这里插入图片描述
就可以访问到这个代币名称了
然后

token.symbol()

在这里插入图片描述
就可以看到我们定义的符号

然后

token.decimals()

在这里插入图片描述
它给我们转换成 这个uint内容了

然后就是

token.totalSupply()

但是内容会发生一些变化
在这里插入图片描述
但是不用担心 这是没问题的

这样 其实就差不多了 但是 其实后面还有一个 balanceOf
在这里插入图片描述
这个东西搞起来就真比较麻烦了

就是 我们每个账户 下面对应的余额是多少
方法传入的是账号的地址
下面就是返回来的余额值

其实 我们可以模拟一种清空

Object public userList = {
    
    
    "admin": 5000,
    "gangdan": 1000,
    "zhanglei": 2000
}

我们这里设置一个对象 这样 当他 token.userList(“用户地址”)
就会返回余额
当然这种写法在solidity中是不能直接用的
但大体可以用这个思路

我们可以这样写 先声明一个mapping类型的变量

mapping(address => uint256) public balanceOf;

这个比较像 java的 哈希map集合
键值对应 这里 我们声明名称键 对应值 值是uint256类型
然后 名称叫 balanceOf

然后在constructor 对象初始化时 赋值

balanceOf[msg.sender] = totalSupply;

在这里插入图片描述
这里 这个msg.sender
之前 我们web3操作智能合约也出现过 那时可以拿到用户列表第一个
这个也差不多 我们说过 部署智能合约 需要消耗燃料值 我们不设置 他就默认取扣第一个的
那么 这个 msg.sender 拿到的就也是这个用户
然后 我们之前讲过totalSupply是总量 直接这样写相当于 发布者拥有所有的代币

然后下面又来一个transfer 发送的方法
在这里插入图片描述因为你不可能一直放在发布者这里啊 是不是?
我们这样写

function transfer(address _to, uint256 _value) public returns (bool success) {
    
    
    balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value);
    balanceOf[_to] = balanceOf[_to].add(_value);
}

但其实刚写这个地方会报错
因为我们模块中暂时没有这些函数

我们需要引入一个库
输入

npm install @openzeppelin/contracts

这个库非常强大 大家可以自己了解一下
在这里插入图片描述

然后 我们在文件最上面引入

import "@openzeppelin/contracts/utils/math/SafeMath.sol";

using SafeMath for uint256;

在这里插入图片描述
这样 我们去使用那两个函数的地方就不报错了
在这里插入图片描述
这里 transfer的业务并不复杂 balanceOf[msg.sender] 拿到当前正在操作的人
然后 将他减少_value数量的代币
然后 用balanceOf[_to] _to代币 我们需要操作的用户地址 给他加上 _value对应数量的代币

这里可能有个误解
我们constructor时 用msg.sender 拿到的是发布这个合约的用户
但 在transfer中拿到的msg.sender 是调用这个函数的用户
不过 我们这里肯定都是用的我们第一个用户 因为我们没有操作切换登录用户

但是这里需要注意的是 如果我们当前用户都没有等于_value的数值 哪那什么处理这个业务是吧?
所以 我们要在外面加个if
在这里插入图片描述
就是简单在外层加个大于等于的判断
在这里插入图片描述
但是 比起if solidity 中处理这种业务 有种更合适的语法 叫require

require的业务功能是 如果你给他的条件是 真(true) 则它继续往下走 如果不是 则直接抛出一个异常 而且这个异常会被我们区块链记录下来

然后 我们再严谨一点

require(_to != address(0));

在这里插入图片描述
因为这个地址是前端传过来的 我们还是要简单判断一下 虽然 他是不是有效地址 这个并不能确定 但是总归有点用
你可以理解为 != null

然后 这里我们还会注意到 这个transfer是有returns的
bool success 要求返回一个布尔类型的值
在这里插入图片描述
如果他能走完全部业务逻辑 我们返回一个 true 表示成功

但是文档这里 又说了 我们必须触发一个Transfer event.的事件
在这里插入图片描述
我们下拉文档找到一个Events
在这里插入图片描述
我们先将这个文档上的事件接口抄到自己的代码中来

event Transfer(address indexed _from, address indexed _to, uint256 _value)

在这里插入图片描述
这个你可以理解为 就是调用一下日志记录我们发送的操作
到时 所有的操作记录 都会在区块链的日志中可以看到

我们之前中心化 或许 有些人可以将自己的操作记录给他干掉 甚至说 有些互联网公司平台操作内部有鬼
但是 我们区块链是真的记录上去就很难去掉了 这个记录也随时可以被调出来

然后 我们在 transfer 中调用它
或者可以说 只要区块链还在 这个记录就在

emit Transfer(msg.sender,_to,_value);

在这里插入图片描述
这样 我们的业务就好了 这里 我们做个小小的测试
我们就不操作终端了 太麻烦了

我们找到根目录下的 scripts 没有就创建一个 下面来一个test.js
在这里插入图片描述
我们终端先执行

truffle migrate --reser

重新部署一下智能合约
在这里插入图片描述然后我们 test.js代表编写如下

const GrToken = artifacts.require("grToken.sol")

module.exports = async function(callback) {
    
    
    const grTokenDai = await GrToken.deployed();
    let res1 = await grTokenDai.balanceOf("0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974");
    console.log(res1);
    callback()
}

这里 大家需要注意 0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974 是我ganache环境下的第一个账号的公钥地址
你们也要根据情况去填写 就是查看一下这个账号下的 getoken数
我们终端运行

truffle exec .\scripts\test.js  

在这里插入图片描述
这里 我们前面输出的token.totalSupply() 共有量就到这来了

说明 我们constructor中的逻辑是完成了
在这里插入图片描述
但是这个单位 显然看着有点头疼

我们可以改成这样

const GrToken = artifacts.require("grToken.sol")

const towei = (bn)=> {
    
    
    return web3.utils.fromwei(bn,"ether");
}

module.exports = async function(callback) {
    
    
    const grTokenDai = await GrToken.deployed();
    let res1 = await grTokenDai.balanceOf("0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974");
    console.log(towei(res1));
    callback()
}

很多人就会说 web3不是没引入吗?
这个大家可以放心 测试环境下 web3的方法是可以顺便用的

我们再次运行

truffle exec .\scripts\test.js  

这次 单位就没问题了
在这里插入图片描述
那么 现在能确定的是 balanceOf确实是好用

接下来 测试 transfer

但是这里有个点比较要命
我们没有登录授权 transfer它拿不到当前登录的用户
我们可以这样写

这里 我们将test.js代码更改如下

const GrToken = artifacts.require("grToken.sol")

const toWei = (bn) => {
    
    
  return web3.utils.fromWei(bn, "ether");
}
const inWei = (bn) => {
    
    
    return web3.utils.toWei(bn.toString(), "ether");
}

module.exports = async function(callback) {
    
    
    const grTokenDai = await GrToken.deployed();
    let res1 = await grTokenDai.balanceOf("0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974");
    console.log(toWei(res1));

    await grTokenDai.transfer("0x096A90463E48723d3631b3291Dd76b6BA425eD4e",inWei(10000),{
    
    
        from: "0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974"
    });
    let res2 = await grTokenDai.balanceOf("0x096A90463E48723d3631b3291Dd76b6BA425eD4e");
    console.log(toWei(res2),"第二个用户");
    let res3 = await grTokenDai.balanceOf("0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974");
    console.log(toWei(res3),"第一个用户");
    callback()
}

inWei就是一个反向转换 transfer在测试环境 我们这就这样用 第一个参数 需要发送给谁 第二个发送多少 第三个是个对象
注意 0x096A90463E48723d3631b3291Dd76b6BA425eD4e 也是一个我ganache模拟出来的用户地址 毕竟这个肯定需要两个用户才能操作嘛
{
from: 当前登录用户地址
}
然后 我们再次终端运行

truffle exec .\scripts\test.js  

在这里插入图片描述
直接起飞啊 起飞

猜你喜欢

转载自blog.csdn.net/weixin_45966674/article/details/131806726