上一篇(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个方法包括:
- 返回token的名字
function name() view returns (string name)
- 返回token的代码
function symbol() view returns (string symbol)
- 返回token的小数点位数
function decimals() view returns (uint8 decimals)
- 返回供应总量
unction totalSupply() view returns (uint256 totalSupply)
- 查询某个地址的余额
function balanceOf(address _owner) view returns (uint256 balance)
- 给某个地址转账,如果余额不足,该方法必须抛出异常而不是返回false
function transfer(address _to, uint256 _value) returns (bool success)
- 从from地址转账到to地址,该方法用于委托给其他合约代理你转账
function transferFrom(address _from, address _to, uint256 _value) returns (bool success)
- 允许多次从您的帐户委托到_spender地址,最高_value金额。如果再次调用此函数,则会覆盖当前允许值_value
function approve(address _spender, uint256 _value) returns (bool success)
- 返回_spender仍允许退出的金额_owner
function allowance(address _owner, address _spender) view returns (uint256 remaining)
2个事件包括:
- 转移令牌时必须触发,包括零值转移,注意:创建新token的合约应该触发Transfer事件,即从_from地址设置为0x0创建token的时候
event Transfer(address indexed _from, address indexed _to, uint256 _value)
- 委托转账必须触发
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
二、代码解释
代码包括几部分:
- 安全的数学计算库
- contract类 ERC20,主要针对ERC20规范定义方法和事件
- contract类StandardToken继承ERC20,定义具体的方法实现
- contract 类MyTokenERC20集成StandardToken,定义名词,代号,总供应量和创建token
三、彩蛋
上面介绍了笔者写的一个生产级别的ERC20发币代码,但是这个代码不支持ico,锁仓,随机空投等等功能。笔者把收藏的更多的代码分享出来,作为彩蛋回馈给大家,点击下面的链接即可。
1.官网推荐的ERC20实现:
- https://github.com/OpenZeppelin/openzeppelin-solidity/tree/master/contracts/token/ERC20
- https://github.com/ConsenSys/Tokens/blob/master/contracts/eip20/EIP20.sol
- 整理大部分ERC20代币,大概500种:
https://github.com/2liang/ERC20ContractCodeLibrary/tree/master/SourceCode
为了更好的和大家交流,笔者建了一个知识星球,原价99元。为感谢大家的支持,现免费开放,可私信我,我拉你加入星球。

网友评论