《我学区块链》—— 九、ERC20代币

九、ERC20代币

1、什么是ERC20

       ERC20是以太坊上的一种代币标准,遵循该标准的代币合约,匀可以由一套以太坊代币钱包代码来调用,其带来了良好的兼容性。
       ERC20定义的接口方法如下:

pragma solidity ^0.4.21;

contract ERC20 {
    function name() public view returns (string);
    function symbol() public view returns (string);
    function decimals() public view returns (uint8);
    function totalSupply() public view returns (uint);
    function balanceOf(address _owner) public returns (uint);
    function transfer(address _to, uint _value) public returns (bool);
    function transferFrom(address _from, address _to, uint _value) public returns (bool);
    function approve(address _spender, uint _value) public returns (bool);
    function allowance(address _owner, address _spender) public returns (uint);
    event Transfer(address indexed _from, address indexed _to, uint _value);
    event Approval(address indexed _owner, address indexed _spender, uint _value);
}

name()

       ERC20代币的名字,例如”My test token”。

symbol()

       代币的简称,例如:MTT、EOS,这个是我们一般在代币交易所看到的名字。

decimals()

       token 精确到小数点后几位。比如如果设置为3,就是支持0.001表示。

totalSupply()

       token 总供应量。

balanceOf(address _owner)

       某地址(账户)的账户余额。

transfer(address _to, uint _value)

       从代币合约的调用者地址上转移 _value 数量的 token 到的地址 _to,并且必须触发 Transfer 事件。

transferFrom(address _from, address _to, uint _value)

       从地址 _from 发送数量为 _value 的 token 到地址 _to,必须触发 Transfer 事件。transferFrom 方法用于允许合同代理某人转移 token。条件是 from 账户必须经过了approve。

approve(address _spender, uint _value)

       允许 _spender 多次取回您的帐户,最高达 _value 金额。 如果再次调用此函数,它将以 _value 覆盖当前的余量。

allowance(address _owner, address _spender)

       返回 _spender 仍然被允许从 _owner 提取的金额。

2、编写一个ERC20代币合约

       这里给出一种 ERC20 代币合约的实现:

contract TokenDemo is ERC20 {

    string public name;                 // 名称,例如 "My test token"
    uint8 public decimals;              // 返回 token 使用的小数点后几位。比如如果设置为 3,就是支持 0.001 表示.
    string public symbol;               // token 简称,like MTT
    uint public totalSupply;

    mapping(address => uint256) balances;
    mapping(address => mapping(address => uint256)) allowed;

    constructor(uint256 _initialAmount, string _tokenName, uint8 _decimalUnits, string _tokenSymbol) public {
        totalSupply = _initialAmount * 10 ** uint256(_decimalUnits);
        // 设置初始总量
        balances[msg.sender] = totalSupply;
        // 初始token数量给予消息发送者,因为是构造函数,所以这里也是合约的创建者

        name = _tokenName;
        decimals = _decimalUnits;
        symbol = _tokenSymbol;
    }

    function transfer(address _to, uint256 _value) public returns (bool success) {
        // 默认 totalSupply 不会超过最大值 (2^256 - 1).
        // 如果随着时间的推移将会有新的 token 生成,则可以用下面这句避免溢出的异常
        require(balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]);
        require(_to != 0x0);
        balances[msg.sender] -= _value;     // 从消息发送者账户中减去 _value 数量的 token
        balances[_to] += _value;            // 往接收账户增加 _value 数量的 token
        emit Transfer(msg.sender, _to, _value);     // 触发转币交易事件
        return true;
    }

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value);
        balances[_to] += _value;        // 接收账户增加 _value 数量的 token
        balances[_from] -= _value;      // 支出账户 _from 减去 _value 数量的 token
        allowed[_from][msg.sender] -= _value;   // 消息发送者可以从账户 _from 中转出的数量减少 _value
        emit Transfer(_from, _to, _value);      // 触发转币交易事件
        return true;
    }

    function balanceOf(address _owner) public returns (uint256) {
        return balances[_owner];
    }

    function approve(address _spender, uint256 _value) public returns (bool) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    function allowance(address _owner, address _spender) public returns (uint256 remaining) {
        return allowed[_owner][_spender];   // 允许 _spender 从 _owner 中转出的 token 数
    }
}

       注:TokenDemo is ERC20 是声明实现某接口,这里实现了我们前面的 ERC20 接口。

3、zeppelin-solidity

       zeppelin-solidity 之于 ERC20 代币智能合约就类似 Spring Boot 之于 JAVA 开发,是一个事实上的业界标准库,对于一些较通用的方法已经做了比较严谨的实现,而我们只需要做另外的部分。接下来我们使用 zeppelin-solidity 来开发一个智能合约。

sudo npm install -g truffle yarn
mkdir tutorial-token
cd tutorial-token
truffle unbox tutorialtoken
yarn add zeppelin-solidity dotenv
yarn install

       解释一下,首先使用 npm 全局安装 truffle 及 yarn,前者是智能合约的开发框架,包含测试、部署等功能,前面的文章已经介绍过;后者是 facebook 开发的一款前端包管理器,相对于 npm 具有更好的版本控制及性能。后面使用 truffle 的 unbox 命令来使用 tutorialtoken 项目作为本次开发 ERC20 代币合约的骨架,之后安装 zeppelin-solidity;最后使用 yarn install 补全所有项目缺失的包。

       使用 WebStorm 打开工程,在 contracts 目录下新建文件 TutorialToken.sol,内容如下:

pragma solidity ^0.4.21;

import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol';

contract TutorialToken is StandardToken {
    string public name = "TutorialToken";
    string public symbol = "HT";
    uint8 public decimals = 2;
    uint public INITIAL_SUPPLY = 10 ** 12;  // equal 10 ^ 12
    constructor() public {
        totalSupply_ = INITIAL_SUPPLY;
        balances[msg.sender] = INITIAL_SUPPLY;
    }
}

       在 migrations 目录下新建 2_deploy_contracts.js,内容如下:

var TutorialToken = artifacts.require("TutorialToken");

module.exports = function(deployer) {
    deployer.deploy(TutorialToken);
}

       修改 src/js/app.js 中 new Web3.providers.HttpProvider(‘http://127.0.0.1:8545‘) 及 truffle.js 中 networks.development.host 及 port 为 127.0.0.1:8545。

app.js

truffle.js

       命令行执行以下命令:

sudo yarn global add ganache-cli
ganache-cli

       新建命令行进入到项目目录:

truffle migrate --reset
npm run dev

       执行成功的话,会自动打开浏览器页面,在 Address 框输入 0xf17f52151EbEF6C7334FAD080c5704D77216b732,Amount 框输入1000,并点击 Transfer,会发现 Balance 余额减少了,说明本次 ERC20 代币编写、部署、功能匀没问题。

tutorialtoken

       眼尖的小伙伴可能会发现,合约中 name,totalSupply 都没有编写对应的方法呢?这是因为 solidity 会自动给 public 变量生成同名的 getter 接口。

       注:想直接看效果的小伙伴也可以直接下载笔者的合约代码: Gitee tutorial-token
       

猜你喜欢

转载自blog.csdn.net/xuguangyuansh/article/details/80385450