美文网首页区块链研习社区块链思维区块链大学
零基础学习以太坊开发-第一个生产级别ERC20合约

零基础学习以太坊开发-第一个生产级别ERC20合约

作者: jerry的技术与思维 | 来源:发表于2018-09-07 22:59 被阅读27次

上一篇(https://www.jianshu.com/p/7723e2ffcf3e)我们简单的介绍了一个非常简单的智能合约的编写,部署和使用,但是这个例子太简单,只能作为简单的demo来学习。

今天,我们来讲讲如何编写一个实际可用,并且没有安全漏洞的生产级别的ERC20代币合约,当然很多人通过百度可以搜索到一堆发币的合约代码,但是大部分都是有安全漏洞的,达不到生产级别。

废话不少,先上完整代码:

pragma solidity ^0.4.24;

library SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
  function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
    if (_a == 0) {
      return 0;
    }

    c = _a * _b;
    assert(c / _a == _b);
    return c;
  }

  /**
  * Integer division of two numbers, truncating the quotient.
  */
  function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
    return _a / _b;
  }

  /**
  * Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
    assert(_b <= _a);
    return _a - _b;
  }

  /**
  *  Adds two numbers, throws on overflow.
  */
  function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
    c = _a + _b;
    assert(c >= _a);
    return c;
  }
}

contract ERC20 {
  uint256 public totalSupply;

  function balanceOf(address _who) public view returns (uint256);

  function allowance(address _owner, address _spender) public view returns (uint256);

  function transfer(address _to, uint256 _value) public returns (bool);
  
  function approve(address _spender, uint256 _value) public returns (bool);

  function transferFrom(address _from, address _to, uint256 _value) public returns (bool);

  event Transfer( address indexed from, address indexed to,  uint256 value);

  event Approval(address indexed owner, address indexed spender, uint256 value);
  
  event Burn(address indexed from, uint256 value);
}


contract StandardToken is ERC20 {
  using SafeMath for uint256;

  mapping(address => uint256) balances;

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


  /**
  * Gets the balance of the specified address.
  * @param _owner The address to query the the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address _owner) public view returns (uint256) {
    return balances[_owner];
  }

  /**
   *  Function to check the amount of tokens that an owner allowed to a spender.
   * @param _owner address The address which owns the funds.
   * @param _spender address The address which will spend the funds.
   * @return A uint256 specifying the amount of tokens still available for the spender.
   */
  function allowance(address _owner, address _spender) public view returns (uint256){
    return allowed[_owner][_spender];
  }

  /**
  * Transfer token for a specified address
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_value <= balances[msg.sender]);
    require(_to != address(0));

    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    emit Transfer(msg.sender, _to, _value);
    return true;
  }

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

  /**
   *  Transfer tokens from one address to another
   * @param _from address The address which you want to send tokens from
   * @param _to address The address which you want to transfer to
   * @param _value uint256 the amount of tokens to be transferred
   */
  function transferFrom(address _from, address _to, uint256 _value) public returns (bool){
    require(_value <= balances[_from]);
    require(_value <= allowed[_from][msg.sender]);
    require(_to != address(0));

    balances[_from] = balances[_from].sub(_value);
    balances[_to] = balances[_to].add(_value);
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    emit Transfer(_from, _to, _value);
    return true;
  }

  /**
   * Increase the amount of tokens that an owner allowed to a spender.
   * approve should be called when allowed[_spender] == 0. To increment
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * @param _spender The address which will spend the funds.
   * @param _addedValue The amount of tokens to increase the allowance by.
   */
  function increaseApproval(address _spender, uint256 _addedValue) public returns (bool) {
    allowed[msg.sender][_spender] = (
    allowed[msg.sender][_spender].add(_addedValue));
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

  /**
   *  Decrease the amount of tokens that an owner allowed to a spender.
   * approve should be called when allowed[_spender] == 0. To decrement
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * @param _spender The address which will spend the funds.
   * @param _subtractedValue The amount of tokens to decrease the allowance by.
   */
  function decreaseApproval(address _spender,  uint256 _subtractedValue) public returns (bool) {
    uint256 oldValue = allowed[msg.sender][_spender];
    if (_subtractedValue >= oldValue) {
      allowed[msg.sender][_spender] = 0;
    } else {
      allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
    }
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }
  
   /**
     * Destroy tokens
     *
     * Remove `_value` tokens from the system irreversibly
     *
     * @param _value the amount of money to burn
     */
    function burn(uint256 _value) public returns (bool success) {
        require(balances[msg.sender] >= _value);   
        balances[msg.sender] = balances[msg.sender].sub(_value);          
        totalSupply = totalSupply.sub(_value);                      
        emit Burn(msg.sender, _value);
        return true;
    }

    /**
     * Destroy tokens from other account
     *
     * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
     *
     * @param _from the address of the sender
     * @param _value the amount of money to burn
     */
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balances[_from] >= _value);                
        require(_value <= allowed[_from][msg.sender]);    
        balances[_from] = balances[_from].sub(_value);                        
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);             
        totalSupply = totalSupply.sub(_value);                              
        emit Burn(_from, _value);
        return true;
    }
}

contract MyTokenERC20 is StandardToken {
    // Public variables of the token
    string public name = "My First ERC20 Token";
    string public symbol = "MFET";
    uint8 constant public decimals = 18;
    uint256 constant public initialSupply = 21000000;

    constructor() public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  
        balances[msg.sender] = totalSupply;               
        emit Transfer(address(0), msg.sender, totalSupply);
    }
}

这些代码是什么意思呢?

一、ERC20(EIP-20)说明

ERC20是一个标准的token接口规范:A standard interface for tokens
该接口的官网说明在下面的链接:https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md

我们来简单介绍下这个接口规范,规范里面定义了9个方法和2个事件
9个方法包括:

  1. 返回token的名字

function name() view returns (string name)

  1. 返回token的代码

function symbol() view returns (string symbol)

  1. 返回token的小数点位数

function decimals() view returns (uint8 decimals)

  1. 返回供应总量

unction totalSupply() view returns (uint256 totalSupply)

  1. 查询某个地址的余额

function balanceOf(address _owner) view returns (uint256 balance)

  1. 给某个地址转账,如果余额不足,该方法必须抛出异常而不是返回false

function transfer(address _to, uint256 _value) returns (bool success)

  1. 从from地址转账到to地址,该方法用于委托给其他合约代理你转账

function transferFrom(address _from, address _to, uint256 _value) returns (bool success)

  1. 允许多次从您的帐户委托到_spender地址,最高_value金额。如果再次调用此函数,则会覆盖当前允许值_value

function approve(address _spender, uint256 _value) returns (bool success)

  1. 返回_spender仍允许退出的金额_owner

function allowance(address _owner, address _spender) view returns (uint256 remaining)

2个事件包括:

  1. 转移令牌时必须触发,包括零值转移,注意:创建新token的合约应该触发Transfer事件,即从_from地址设置为0x0创建token的时候

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

  1. 委托转账必须触发

event Approval(address indexed _owner, address indexed _spender, uint256 _value)

二、代码解释

代码包括几部分:

  1. 安全的数学计算库
  2. contract类 ERC20,主要针对ERC20规范定义方法和事件
  3. contract类StandardToken继承ERC20,定义具体的方法实现
  4. contract 类MyTokenERC20集成StandardToken,定义名词,代号,总供应量和创建token

三、彩蛋

上面介绍了笔者写的一个生产级别的ERC20发币代码,但是这个代码不支持ico,锁仓,随机空投等等功能。笔者把收藏的更多的代码分享出来,作为彩蛋回馈给大家,点击下面的链接即可。
1.官网推荐的ERC20实现:

  1. 整理大部分ERC20代币,大概500种:
    https://github.com/2liang/ERC20ContractCodeLibrary/tree/master/SourceCode

为了更好的和大家交流,笔者建了一个知识星球,原价99元。为感谢大家的支持,现免费开放,可私信我,我拉你加入星球。

image.png

相关文章

网友评论

  • 羽一兰:我从大学就一直特别崇拜思维方式和我不一样的人,尤其是我看不到搞不明白的方向🙈

本文标题:零基础学习以太坊开发-第一个生产级别ERC20合约

本文链接:https://www.haomeiwen.com/subject/knshgftx.html