背景

区块链 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);
	}
}
内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/pickmea/p/16253550.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!