以太坊ERC-20协议详解


区块链学习: https://github.com/xianfeng92/Love-Ethereum

-------------------------------------------------------------------

ERC20是以太坊定义的一个[代币标准](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md), 利用以太坊的智能合约可以轻松编写出属于自己的代币,这里的代币可以代表在平台或者社区的所拥有的权益。


# 太坊的代币标准

使用代币合约标准可以在以太坊上发行在区块链应用(Dapp)上所使用的代币。

## Token

## Methods

NOTE: Callers MUST handle false from returns (bool success). Callers MUST NOT assume that false is never returned!

调用者必须处理返回值为false的方法

## name
Returns the name of the token


## symbol
Returns the symbol of the token


## decimals
Returns the number of decimals the token uses


## totalSupply
Returns the total token supply


## balanceOf
Returns the account balance of another account with address _owner
返回账户owner中的余额

## transfer
Transfers _value amount of tokens to address _to, and __MUST fire the Transfer event__. The function SHOULD throw if the _from account balance does not have enough tokens to spend

Note Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event

## transferFrom


Transfers _value amount of tokens from address _from to address _to, and MUST fire the Transfer event


The transferFrom method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf. This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies


transferFrom方法用于"转账"流程,允许合约代表您转让token。 例如,这可用于允许合同代表您转让代币或者以子货币收取费用。


Note Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event

## approve

Allows _spender to withdraw from your account multiple times, up to the _value amount. If this function is called again it overwrites the current allowance with _value

允许spender从你的账户中多次"提取"token,直到规定的金额value


## allowance


Returns the amount which _spender is still allowed to withdraw from _owner


返回账户spender可以从合约所有者owner中提取的token数量




## Events


### Transfer


MUST trigger when tokens are transferred, including zero value transfers.


涉及到token转移时必须触发此事件


A token contract which creates new tokens SHOULD trigger a Transfer event with the _from address set to 0x0 when tokens are created


代币合约部署时也需要触发此事件


### Approval


MUST trigger on any successful call to approve(address _spender, uint256 _value)






# 代币合约实例


下面是一个 token 合约的例子,可以直接复制到remix进行合约部署的。


事先说明两点:


1token的发行很简单的,其价值还是要看和token所关联的项目。有很多组织没有项目就胡乱发token。


2token这个概论本身是有很大的价值的,如何发行和利用token,对项目成败也有很多影响。




pragma solidity ^0.4.20;


interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }


contract TokenLBJ {
    // 代币名称,如:"LeBron James"
    string public name;
    // 代币符合,一般用代币名称的缩写,如 LBJ
    string public symbol;
    // 每个代币可细分的到多少位,即最小代币单位。18为默认值
    uint8 public decimals = 18;
    // 代币总供应量,这里指的是一共有多少个以最小单位所计量的代币
    uint256 public totalSupply;


    // 用mapping保存每个地址对应的余额
    mapping (address => uint256) public balanceOf;


    // 存储对账号的控制,有两点需要注意的:1本合约账户可以授权其他账户转移token数量 2 其他账户授权该合约账户转移token数量
    mapping (address => mapping (address => uint256)) public allowance;


    // 事件,用来通知客户端交易发生
    event Transfer(address indexed from, address indexed to, uint256 value);


    // 事件,用来通知客户端代币被销毁
    event Burn(address indexed from, uint256 value);


    /**
     * 初始化构造,初始化后代币全部存储于合约创建者账户
     */
    function TokenLBJ(uint256 initialSupply, string tokenName, string tokenSymbol) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // 供应的份额,份额跟最小的代币单位有关,份额 = 币数 * 10 ** decimals
        balanceOf[msg.sender] = totalSupply;                // 创建者拥有所有的代币
        name = tokenName;                                   // 代币名称
        symbol = tokenSymbol;                               // 代币符号
    }


    /**
     * 代币交易转移的内部实现,只能被本合约调用
     */
    function _transfer(address _from, address _to, uint _value) internal {
        // 确保目地地址不为0x0,因为0x0地址代表的是销毁
        require(_to != 0x0);
        // 确保发送者账户有足够的余额
        require(balanceOf[_from] >= _value);
        // 确保_value为正数,如果为负数,那相当于付款者账户钱越买越多~哈哈~
        require(balanceOf[_to] + _value > balanceOf[_to]);


        // 交易前,双方账户余额总和
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // 将发送方账户余额减value
        balanceOf[_from] -= _value;
        // 将接收方账户余额加value
        balanceOf[_to] += _value;
        //通知客户端交易发生
        Transfer(_from, _to, _value);


        // 用assert来检查代码逻辑,即交易前后双发账户余额的和应该是相同的
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }


    /**
     * 从合约创建交易者账号发送`_value`个代币到 `_to`账号
     * 这种类似于现在很多项目的空头吧,很多空气项目其实也就是在以太坊上发个币,然后就割韭菜,所以学学区块链技术还是很有必要的
     * @param _to 接收者地址
     * @param _value 转移数额
     */
    function transfer(address _to, uint256 _value) public {
        //_from始终是合约创建者的地址
        _transfer(msg.sender, _to, _value);
    }


    /**
     *批量转帐固定金额
    /
    function batchTransfer(address[] _to, uint _value) public{
        require(address.length > 0)
        for(uint i=0 ; i< address.length; i++){
           _transfer(msg.sender,address[i], _value);
         }
    }


    /**
     * Transfer tokens from other address
     * Send '_value' tokens to '_to' on behalf of '_from'
     * @param _from 发送者地址
     * @param _to 接收者地址
     * @param _value 转移数额
     */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        // _from 所授权 msg.sender 转移的代币数量
        require(_value <= allowance[_from][msg.sender]);
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }


    /**
     *
     * 允许发送者`_spender` 花费不多于 `_value` 个代币
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        //具体结构如下:(msg.sender --> allowance[msg.sender])--> mapping(address => uint)
        // msg.sender 允许 _spender 最多花费的代币数量 
        allowance[msg.sender][_spender] = _value;
        return true;
    }


    /**
     * 设置允许一个地址(合约)以我(创建交易者)的名义可最多花费的代币数。
     *
     * @param _spender 被授权的地址(合约)
     * @param _value 最大可花费代币数
     * @param _extraData 发送给合约的附加数据
     */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData)
        public
        returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            // 通知合约
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }


    /**
     * 销毁合约账户中指定数量的代币
     */
    function burn(uint256 _value) public returns (bool success) {
        // 检查合约账户是否有足够的代币
        require(balanceOf[msg.sender] >= _value);
        // 将合约账户余额减少value
        balanceOf[msg.sender] -= _value;
        // 对应代币总供应量也应该减少
        totalSupply -= _value;
        Burn(msg.sender, _value);
        return true;
    }


    /**
     * 销毁用户账户中指定个代币
     *
     * 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(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        Burn(_from, _value);
        return true;
    }
}




# 给代币增加可管理、增发、兑换、冻结等功能




contract owned{
  
   address public owner;
   
   function owned(){
        owner = msg.sender;
    }


   //
   modifier onlyOwner {
        require(msg.sender == owner);
        _;
      }


   function transferOwnership(address newOwner) onlyOwner{
        owner = newOwner;
     }
 }


该合约中加入了一个函数修改器(Function Modifiers)onlyOwner,函数修改器是一个合约属性,可以被继承,还能被重写。它用于在函数执行前检查某种前置条件。


此时让TokenLBJ继承owned,使其拥有onlyOwner修改器。


## 代币增发




function mintToken(address target, uint256 mintedAmount) onlyOwner{


      balanceof[target] += mintedAmount;
      totalSupply += mintedAmount;
      Transfer(0,owner,mintedAmount);
      Transfer(owner, target, mintedAmount);
 }


注意onlyOwner修改器添加在函数末尾,这表示只有ower才能调用这用函数。他的功能很简单,就是给指定的账户增加代币,同时增加总供应量。


## 资产冻结


有时为了监管的需要,需要实现冻结某些账户,冻结后,其资产仍在账户,但是不允许交易,直到解除冻结。给合约添加以下的变量和方法(可以添加到合约的任何地方,但是建议把mapping加到和其他mapping一起,event也是如此):


mapping(address => bool) public frozenAccount;


event FrozenFunds(address target, bool frozen);


function freezeAccount(address target, bool freeze) onlyOwner {
    frozenAccount[target] = freeze;
    FrozenFunds(target, freeze);
 }


单单以上的代码还无法冻结,需要把他加入到transfer函数中才能真正生效,因此修改transfer函数。


function transfer(address _to, uint256 _value){
    
    require(!frozenAccount[msg.sender])
    ...
 }


这样在转账前,对发起交易的账号做一次检查,只有没被冻结的账号才能转账。




## 代币买卖(兑换)


可以自己的货币中实现代币与其他数字货币(ether 或其他tokens)的兑换机制。有了这个功能,我们的合约就可以在一买一卖中赚利润了。


先来设置下买卖价格:


uint256 public sellPrice;
uint256 public buyPrice;


function setPrice (uint256 newSellPrice, uint256 newBuyPrice) onlyOwner{
      sellPrice = newSellPrice;
      buyPrice = newBuyPrice;
  }




setPrices()添加了onlyOwner修改器,注意买卖的价格单位是wei(最小的货币单位: 1 eth = 1000000000000000000 wei)。


添加买卖函数:


function buy() payable returns (uint amount){
   
   amount = msg.value / buyPrice;
   require(balanceof[this] >= amount);
   balanceof[msg.sender] += amount;
   balanceof[this] -= amount;
   Transfer(this, msg.sender,amount);
   return amount;
 }


function sell(uint amount) payable returns (uint revenue){


   require(balanceof[msg.sender] >= amount);
   balanceof[this] += amount;
   balanceof[msg.sender] -= amount;
   revenue = amount * sellPrice;
   msg.sender.transfer(revenue);
   Transfer(msg.sender, this, amount);
   return revenue;
 }


加入了买卖功能后,要求我们在创建合约时发送足够的以太币,以便合约有能力回购市面上的代币,否则合约将破产,用户没法先合约卖代币。


参考:[Create your own crypto-currency](https://ethereum.org/token)

猜你喜欢

转载自blog.csdn.net/XFORG1992/article/details/80992739