以太坊ERC-20协议详解

2023-11-12


区块链学习: 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)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

以太坊ERC-20协议详解 的相关文章

随机推荐

  • 强化学习-任务与奖赏(周志华)

    文章目录 什么是强化学习 马尔科夫决策过程 四元组 机器 与 环境 界限 策略两种表示方法及奖赏 2K 摇臂赌博机 探索与利用 贪心法 softmax 什么是强化学习 强化学习 一种学习过程 包含很多学习步骤 经过一段时间才能收获结果 通常
  • 404 not found nginx(dist打包后,刷新和跳转都是404 not found nginx的问题) 解决方案(打包发布在服务器)

    当我们执行了yarn run build之后 生成dist文件 我们将代码放入nginx 1 24 0下面的html中 然后我们就配置conf文件下的nginx conf 配置方面不介绍了 主要问题是因为没有加这句话 问题分析 index
  • MySQL连接查询之内连接、左连接、右连接、自连接

    目录 一 内连接 1 连接查询的介绍 2 内连接查询 二 左连接 1 左连接查询 三 右连接 1 右连接查询 四 自连接 1 自连接查询 一 内连接 1 连接查询的介绍 连接查询可以实现多个表的查询 当查询的字段数据来自不同的表就可以使用连
  • java 电商锁库存实现_电商项目扣减库存方案

    阿里巴巴b2b电商算法实战电子商务 85 3元 包邮 需用券 去购买 gt 各位小宝贝们 大家是不是在面试过程中经常被问到 你电商项目扣减库存时 到底是下单减库存呢 还是付款减库存 那今天给大家出几种解决方案 有不对的地方欢迎批评指正 一
  • 人机交互与智能的思考

    1 智能与交互的起源 霍金曾说过 在过去的20年里 人工智能一直专注于围绕建设智能体所产生的问题 即在特定的情境下 可以感知并行动的各种系统 在这种情况下 智能是一个与统计学 和经济学相关的理性概念 通俗地讲 这是一种做出好的决定计划和推论
  • 【Logstash】【ElasticSearch】【Kibana】安装测试【日志存储】

    Linux日志 var存放日志 log auth 认证 boot 登录 dpkg 深度 Debian软件安装 fontconfig 字体 Logstash功能 Logstash是一个开源的数据收集引擎 具有实时管道功能 接收 处理 转发日志
  • 华为培训04 路由协议

    学习目标 路由分类 路由配置 VLAN路由 1 路由基础 2 VLAN间路由 2 1 三层交换 解决VLAN间通信的一种解决办法就是三层交换 在三层交换机上配置vlanif接口 实现VLAN间路由 如果网络上有多个VLAN 则需要给每一个V
  • 基于springboot开发项目架构之CMS

    1 CMS是什么 CMS Content Management System 即内容管理系统 不同的项目对CMS的定位不同 比如 一个在线教育网站 有些公司认为CMS系统是对所有的课程资源进行管理 而在早期网站刚开始盛行时很多公司的业务是网
  • Spring Cloud Alibaba + mybatis + Element UI 前后端分离 分布式微服务高并发数据平台化(中台)思想+多租户saas企业开发架构技术选型和设计方案

    基于Spring Cloud Alibaba 分布式微服务高并发数据平台化 中台 思想 多租户saas设计的企业开发架构 支持源码二次开发 支持其他业务系统集成 集中式应用权限管理 支持拓展其他任意子项目 一 架构技术选型 核心框架 Spr
  • [云原生专题-41]:K8S - 核心概念 - Service业务的统一网关接口Ingress详解、安装、常见操作命令

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 122804728 目录 前言 第1章
  • 面向切面编程SpringAop入门案例和实现机制

    代理与SpringAop springAop概述 SpringAop术语 SpringAop的实现机制 JDK实现动态代理 CGlib实现动态代理 springAop代理的实现 xml springAop代理的实现 注解 切点修饰语法 sp
  • ROS中订阅(Subscribe)最新消息以及对消息队列的浅谈

    ROS中订阅 Subscribe 最新消息以及对消息队列的浅谈 机器人应用中难免会遇到运算起来很费时间的操作 比如图像的特征提取 点云的匹配等等 有时候 不可避免地 我们需要在ROS的Subscriber的Callback回调函数中进行这些
  • 作用域和堆内存的区别

    作用域是函数执行的时候产生fn 函数执行的时候首先会开辟一个新的内存空间叫栈内存 环境或作用域 数据类型在赋值的时候会开辟一个新的内存空间叫堆内存 存放代码块的 二者都会形成一个内存地址 生成对象的单例模式 优势 每个对象都是独立的 即便属
  • @SuppressWarnings("resource")作用

    1 实例 SuppressWarnings resource public static void main String args Scanner input new Scanner System in 写代码时 input 警告加上这个
  • 若隐若现的芯片

    先看效果 再看代码
  • 【java语法基础】常量与变量、数据类型,以及数据类型的转换

    常量 就是值永远不被改变的量 声明一个常量 需要用final关键字修饰 具体格式 final 常量类型 常量标识符 常量值 例如 final int PIE 18 注 在定义一个常量标识符时 所有的字符都要大写 如果常量标识符由多个单词组成
  • 用python实现数字图片识别神经网络--实现网络训练功能

    上节我们完成了神经网络基本框架的搭建 当时剩下了最重要的一个接口train 也就是通过读取数据自我学习 进而改进网络识别效率的功能尚未实现 从本节开始 我们着手实现该功能 自我训练过程分两步走 第一步是计算输入训练数据 给出网络的计算结果
  • git查看日志

    目录 引言 git查看该项目提交记录 查看指定条数的记录 显示提交的差异 提交的简略信息 按行显示提交信息 按照指定格式显示记录 指定文件的提交记录 指定字符串或函数的提交记录 示例 引言 有时需要对之前所做的一些修改查看记录 这里是查看g
  • STM32F407ZGT6控制舵机(采用高级定时器8)

    前言 32单片机给舵机供电不足 会出现不稳定的情况 舵机鬼畜 所以要外加电源给舵机供电 利用12v锂电池 通过稳压模块降压到5 5v 提供给舵机 稳压电路的gnd一定要接上32单片机的gnd 不共地虽然能供电但数据线无法传输数据 stm32
  • 以太坊ERC-20协议详解

    区块链学习 https github com xianfeng92 Love Ethereum ERC20是以太坊定义的一个 代币标准 https github com ethereum EIPs blob master EIPS eip