• ERC20 ERC677 代币标准


    背景

    区块链 1.0 btc
    2.0 eth 可编程的时代

    代币是以太坊这类通用编程区块链之上最显著和有用的显著形态
    • 代币标准(ERC20、ERC721主要标准)是实现的最小化标准
    • 使用已存在的标准,可完成合约之前的互操作性
    • 成熟度带来的安全性,代币标准经过实战验证

    参考资料:

    https://eips.ethereum.org/EIPS/eip-20
    https://eips.ethereum.org/erc
    https://github.com/ethereum/EIPs

    ERC20 代币标准

    最小单元

    6个函数 function

    // 获取代币发行总量
    function totalSupply() public view returns (uint256)
    
    // 获取_owner用户代币余额数量
    function balanceOf(address _owner) public view returns (uint256 balance)
    
    // _owner给_to账户转账_value
    function transfer(address _to, uint256 _value) public returns (bool success)
    
    // 通过地址给_to账户转账_value  (_from账户必须授权给调用者权限去操作)
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
    
    // 授权_spender提款,最高金额_value
    function approve(address _spender, uint256 _value) public returns (bool success)
    
    // 返回 _spender 仍然被允许从 _owner 提取的金额。
    function allowance(address _owner, address _spender) public view returns (uint256 remaining)
    

    2个事件 event

    需要在对应的函数中状态变更时,对事件进行相关调用

    // 转账事件
    event Transfer(address indexed _from, address indexed _to, uint256 _value)
      
    // 授权提款事件
    event Approval(address indexed _owner, address indexed _spender, uint256 _value)
    

    可选单元

    3个函数 function

    // name 代币名称 
    function name() public view returns (string)
    
    // symbol 代币符号
    function symbol() public view returns (string)
    
    // decimals 精度
    function decimals() public view returns (uint8)
    
    注意
    • 合约contract关键字之前需要加上abstract
    • 在抽象的函数内,添加virtual关键字
    • 继承函数时,需要对继承的函数加上override修饰符

    没有函数体的函数被称为抽象函数

    合约实例

    ERC20.sol

    pragma solidity ^0.6.0;
    
    //ERC20标准
    abstract contract ERC20 {
    
        function symbol() virtual public view returns (string memory);
    
        //六个函数
        function totalSupply() virtual public view returns (uint256 totalSupply);
        function balanceOf(address _owner) virtual public view returns (uint256 balance);
        function transfer(address _to, uint256 _value) virtual public returns (bool success);
        function transferFrom(address _from, address _to, uint256 _value) virtual public returns (bool success);
        function approve(address _spender, uint256 _value) virtual public returns (bool success);
        function allowance(address _owner, address _spender) virtual public view returns (uint256 remaining);
    
        // 2个事件event
        event Transfer(address indexed _from, address indexed _to, uint256 _value);
        event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    }
    
    

    pkCoin

    pragma solidity ^0.6.0;
    
    import "./ERC20.sol";
    
    contract pkCoin is ERC20 {
    
        uint256 _totalSupply;
        string  public _symbol;
        address public _owner;
        mapping(address=>uint256) _balances;
        mapping(address => mapping(address => uint256) ) _approve;
    
        event Transfer(address indexed _from, address indexed _to, uint256 _value);
        event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    
        constructor(uint256 totalSupply, string memory symbol) public {
            _totalSupply = totalSupply;
            _symbol = symbol;
            _owner = msg.sender;
        }
        
        // 自定义修饰符
        modifier onlyOwner(){
            require(msg.sender == _owner);
            // _代指的是使用函数修改器的函数体
            _;
        }
    
        function symbol() virtual override public view returns (string memory){
            return _symbol;
        }
    
    
        // 初始化空投
        function airDrop(address _to, uint256 _value) onlyOwner public{
            require(_to != address(0) && _value > 0 );
            _balances[_to] = _value;
            _totalSupply += _value;
        }
    
        // override复写关键字
        function totalSupply() override public view returns (uint256 totalSupply) {
            totalSupply = _totalSupply;
            return totalSupply;
    
        }
    
        function balanceOf(address _owner) override public view returns (uint256 balance){
            require(_owner != address(0), "owner should not be empty !");
            return _balances[_owner];
    
        }
    
        function transfer(address _to, uint256 _value) override public returns (bool success){
            require( _to != address(0), "_to should be not empty!");
            require( _balances[msg.sender] >= _value || _value >0, "value should be valid!");
            _balances[msg.sender] -= _value;
            _balances[_to] += _value;
            success = true;
            emit Transfer(msg.sender, _to, _value);
            return success;
    
        }
    
        function transferFrom(address _from, address _to, uint256 _value) override public returns (bool success){
            require(_from != address(0) && _to != address(0));
            require(_balances[msg.sender] >= _value);
            
            // require( _approve[_from][msg.sender] >= _value);
            _approve[_from][msg.sender] -= _value;
            _balances[_to] += _value;
            _balances[_from] -= _value;
            success = true;
            emit Transfer(_from, _to, _value);
            return success;
    
        }
    
        function approve(address _spender, uint256 _value) override public returns (bool success){
            require(_spender  != address(0));
            require(_balances[msg.sender] >= _value && _value >0 );
            _approve[msg.sender][_spender] += _value;
            _balances[msg.sender] -= _value;
            success = true;
            emit Approval(msg.sender, _spender, _value);
            return success;
    
        }
    
        function allowance(address _owner, address _spender) override public view returns (uint256 remaining) {
            remaining = _approve[_owner][_spender];
            return remaining;
    
        }
    }
    
    

    ERC677 代币标准

    前述

    ERC677 标准是 ERC20 的一个扩展,它继承了 ERC20 的所有方法和事件。

    差异

    transferAndCall

    增加了一个transferAndCall 方法

    function transferAndCall(address receiver, uint amount, bytes data) returns (bool success)
    

    这个方法比 ERC20 中的 transfer 方法多了一个 data 字段,这个字段用于在转账的同时,携带用户自定义的数据。在方法调用的时候,还会触发Transfer(address,address,uint256,bytes) 事件,记录下发送方、接收方、转账金额以及附带数据。

    onTokenTransfer

    完成转账和记录日志之后,代币合约还会调用接收合约的onTokenTransfer方法,用来触发接收合约的逻辑。这就要就接收ERC677代币的合约必须实现onTokenTransfer方法,用来给代币合约调用

    function onTokenTransfer(address from, uint256 amount, bytes data) returns (bool success)
    

    接收合约就可以在这个方法中定义自己的业务逻辑,可以在发生转账的时候自动触发。换句话说,智能合约中的业务逻辑,可以通过代币转账的方式来触发自动运行。这就给了智能合约的应用场景有了很大的想象空间。

    参考:
    LINK 代币合约 https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code
    https://github.com/ethereum/EIPs/issues/677

    漏洞案例-整数溢出

    溢出原理

    类型

    • 乘法溢出
    • 加法溢出
    • 减法溢出

    demo

    • 缺陷代码
    pragma solidity ^0.6.0;
    
    contract overFlow{
        //加法溢出
        //如果uint256 类型的变量达到了它的最大值(2**256 - 1),如果在加上一个大于0的值便会变成0
        function add_overflow() public view returns (uint256 _overflow) {
            uint256 max = 2**256 - 1;
            return max + 1;
        }
    
    
    	//减法溢出
    	//如果uint256 类型的变量达到了它的最小值(0),如果在减去一个小于0的值便会变成2**256-1(uin256类型的最大值)
    	function sub_underflow() public view returns (uint256 _underflow) {
        	uint256 min = 0;
        	return min - 1;
    	}
        
        //乘法溢出
    	//如果uint256 类型的变量超过了它的最大值(2**256 - 1),最后它的值就会回绕变成0
    	function mul_overflow() public view returns (uint256 _underflow) {
        	uint256 mul = 2**255;
        	return mul * 2;
    	}
    }
    

    整数溢出防护

    • 修复代码
    pragma solidity ^0.6.0;
    
    library SafeMath {
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a * b;
        // assert 和 require 区别在于,require 若失败则会返还给用户剩下的 gas, assert 则不会
        assert(a == 0 || c / a == b);
        return c;
      }
    
      function div(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a / b;
        return c;
      }
    
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    contract safeOverflow{
        using SafeMath for uint256;
        
        //加法溢出
        //如果uint256 类型的变量达到了它的最大值(2**256 - 1),如果在加上一个大于0的值便会变成0
        function add_overflow() public view returns (uint256 _overflow) {
            uint256 max = 2**256 - 1;
            return max.add(1);
        }
    
    
    	//减法溢出
    	//如果uint256 类型的变量达到了它的最小值(0),如果在减去一个小于0的值便会变成2**256-1(uin256类型的最大值)
    	function sub_underflow() public view returns (uint256 _underflow) {
        	uint256 min = 0;
        	return min.sub(1);
    	}
        
        //乘法溢出
    	//如果uint256 类型的变量超过了它的最大值(2**256 - 1),最后它的值就会回绕变成0
    	function mul_overflow() public view returns (uint256 _underflow) {
        	uint256 mul = 2**255;
        	return mul.mul(2);
    	}
    }
    
  • 相关阅读:
    Goroutine被动调度之一(18)
    实战分析一个运行起来会卡死的Go程序
    Go语言调度器之盗取goroutine(17)
    第三章 Goroutine调度策略(16)
    非main goroutine的退出及调度循环(15)
    Go语言调度器之调度main goroutine(14)
    PHP经典面试题之 Redis 内存满了怎么办?
    【PHP】让新人快速理解ThinkPHP6中的事务操作
    面试官:说说swoole+PHP实现自动取消订单,还原库存等操作
    最新整理的PHP高级面试题来啦!【附答案】
  • 原文地址:https://www.cnblogs.com/pickmea/p/16253550.html
Copyright © 2020-2023  润新知