erc20的标准: eips.ethereum.org/EIPS/eip-20…
solidity知名合约系列,会包括三个系列:
1. ERC20,也就是代币相关的;
2. ERC721,也就是nft相关的;
3. DEX,也就是去中心化交易所相关的;
在看合约之前,需要一些:
1. solidity的基础知识;
2. 以太坊的基础知识;
合约主要功能
1. 地址可以拥有token,以及向某个地址转账;
2. 某个地址A可以向某个地址B授权一部分token的额度,允许地址B进行转账操作;
合约间的调用关系
合约解读
Ownable
contract Ownable {
address public owner;
function Ownable() public {
// 构造函数中初始化合约owner地址
owner = msg.sender;
}
modifier onlyOwner() {
// 修饰器,用来保证只有owner才能调用
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
// 只有owner才能转移所有权,并且新owner的地址不能是黑洞地址
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
ERC20Basic
1. 这是个抽象合约,由于abstract关键字在solidity0.6.0版本才引入,所以该合约中没有加abstract关键字;
2. 抽象合约的目的是为了解耦定义与实现;
3. 这里用constant当成函数的修饰符保证状态不可变的用法在solitidy0.5.0中已经不允许使用了
contract ERC20Basic {
uint public _totalSupply;
// 总供应量
function totalSupply() public constant returns (uint);
// 某个地址的余额
function balanceOf(address who) public constant returns (uint);
// 给某个地址转账
function transfer(address to, uint value) public;
// transfer事件
event Transfer(address indexed from, address indexed to, uint value);
}
BasicToken
1. 基础token合约的具体实现
2. 需要实现转账和查余额的功能;
contract BasicToken is Ownable, ERC20Basic {
// unit类型的变量可以使用SafeMath库中的方法
using SafeMath for uint;
mapping(address => uint) public balances;
// additional variables for use if transaction fees ever became necessary
uint public basisPointsRate = 0;
uint public maximumFee = 0;
/**
* @dev Fix for the ERC20 short address attack.
*/
modifier onlyPayloadSize(uint size) {
require(!(msg.data.length < size + 4));
_;
}
function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
// 实际转账要扣除手续费
uint sendAmount = _value.sub(fee);
// 发送方扣钱
balances[msg.sender] = balances[msg.sender].sub(_value);
// 接收方收钱
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
// 手续费归合约的owner
balances[owner] = balances[owner].add(fee);
Transfer(msg.sender, owner, fee);
}
Transfer(msg.sender, _to, sendAmount);
}
function balanceOf(address _owner) public constant returns (uint balance) {
// 获取某个地址的余额
return balances[_owner];
}
}
ERC20
1. 这是个抽象合约;
2. 在ERC20Basic合约的基础上增加了approve操作,用户可以给某个合约授权,允许其transfer自己的token
contract ERC20 is ERC20Basic {
// spender还能从owner上花费多少
function allowance(address owner, address spender) public constant returns (uint);
// 从from向to地址转账value,主要用于合约操作;msg.sender必须是from account已经授权了的
function transferFrom(address from, address to, uint value) public;
// 允许spender最多花费这么多value
function approve(address spender, uint value) public;
// approve事件
event Approval(address indexed owner, address indexed spender, uint value);
}
StandardToken
1. 标准token合约的具体实现
2. 需要实现授权的功能
contract StandardToken is BasicToken, ERC20 {
// 某个地址授权某个地址最多可消费多少
mapping (address => mapping (address => uint)) public allowed;
uint public constant MAX_UINT = 2**256 - 1;
// 被授权者,调用合约从from向to转账
function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
// 首先就要看from地址给被授权者 授权了多少数量
var _allowance = allowed[_from][msg.sender];
// 如果转账数量超过授权数量,则报错
// if (_value > _allowance) throw;
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
if (_allowance < MAX_UINT) {
// 减少授权的数量
allowed[_from][msg.sender] = _allowance.sub(_value);
}
// 真正的转移数量 需要扣除手续费
uint sendAmount = _value.sub(fee);
// from地址【授权方】减钱
balances[_from] = balances[_from].sub(_value);
// to地址 加钱
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
// 手续费归合约owner
balances[owner] = balances[owner].add(fee);
Transfer(_from, owner, fee);
}
Transfer(_from, _to, sendAmount);
}
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
// 在授权前,需要先清空原来的授权
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender, 0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
}
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
// 返回剩余的授权数量
return allowed[_owner][_spender];
}
}
Pausable
该合约用于实现紧急停止合约的功能,该功能是通过修饰器提供给其他合约使用的
contract Pausable is Ownable {
event Pause();
event Unpause();
// 用于标记是否停止
bool public paused = false;
// 修饰器,用于要求状态得是非停止
modifier whenNotPaused() {
require(!paused);
_;
}
// 修饰器,用于要求状态得是停止
modifier whenPaused() {
require(paused);
_;
}
// 非停止的状态下才能停止,且发出事件
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
// 停止的状态下才能启动,且发出事件
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
BlackList
该合约用于实现黑名单
contract BlackList is Ownable, BasicToken {
/////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
function getBlackListStatus(address _maker) external constant returns (bool) {
return isBlackListed[_maker];
}
function getOwner() external constant returns (address) {
return owner;
}
// 黑名单地址的mapping存储
mapping (address => bool) public isBlackListed;
// 只有合约的owner才能增加黑名单地址
function addBlackList (address _evilUser) public onlyOwner {
isBlackListed[_evilUser] = true;
AddedBlackList(_evilUser);
}
// 只有合约的owner才能删除黑名单
function removeBlackList (address _clearedUser) public onlyOwner {
isBlackListed[_clearedUser] = false;
RemovedBlackList(_clearedUser);
}
// 只有合约的owner才能销毁黑名单地址上的数量
function destroyBlackFunds (address _blackListedUser) public onlyOwner {
// 地址必须在黑名单
require(isBlackListed[_blackListedUser]);
// 将地址上的余额清零
uint dirtyFunds = balanceOf(_blackListedUser);
balances[_blackListedUser] = 0;
// 减少总供应量
_totalSupply -= dirtyFunds;
// 发出事件
DestroyedBlackFunds(_blackListedUser, dirtyFunds);
}
event DestroyedBlackFunds(address _blackListedUser, uint _balance);
event AddedBlackList(address _user);
event RemovedBlackList(address _user);
}
UpgradedStandardToken
1. 是个抽象合约
2. 增加了三个必须由合约调用的方法
contract UpgradedStandardToken is StandardToken{
// those methods are called by the legacy contract
// and they must ensure msg.sender to be the contract address
function transferByLegacy(address from, address to, uint value) public;
function transferFromByLegacy(address sender, address from, address spender, uint value) public;
function approveByLegacy(address from, address spender, uint value) public;
}
TetherToken
1. 增加了合约升级的方法
2. 兼容了合约升级的逻辑
contract TetherToken is Pausable, StandardToken, BlackList {
string public name; // token名称
string public symbol; // token标识
uint public decimals; // token精度
address public upgradedAddress;// 升级后的合约地址
bool public deprecated; // 是否废弃
// 初始化,注意这里会初始化总供应量
function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
_totalSupply = _initialSupply;
name = _name;
symbol = _symbol;
decimals = _decimals;
balances[owner] = _initialSupply;
deprecated = false;
}
// 兼容升级
function transfer(address _to, uint _value) public whenNotPaused {
require(!isBlackListed[msg.sender]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
} else {
return super.transfer(_to, _value);
}
}
// 兼容升级
function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
require(!isBlackListed[_from]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
} else {
return super.transferFrom(_from, _to, _value);
}
}
// 兼容升级
function balanceOf(address who) public constant returns (uint) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).balanceOf(who);
} else {
return super.balanceOf(who);
}
}
// 兼容升级
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
} else {
return super.approve(_spender, _value);
}
}
// 兼容升级
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
if (deprecated) {
return StandardToken(upgradedAddress).allowance(_owner, _spender);
} else {
return super.allowance(_owner, _spender);
}
}
// 升级合约
function deprecate(address _upgradedAddress) public onlyOwner {
deprecated = true;
upgradedAddress = _upgradedAddress;
Deprecate(_upgradedAddress);
}
// 获取总的供应量
function totalSupply() public constant returns (uint) {
if (deprecated) {
return StandardToken(upgradedAddress).totalSupply();
} else {
return _totalSupply;
}
}
// 向合约的owner加钱
function issue(uint amount) public onlyOwner {
require(_totalSupply + amount > _totalSupply);
require(balances[owner] + amount > balances[owner]);
balances[owner] += amount;
_totalSupply += amount;
Issue(amount);
}
// 从合约的owner中扣钱
function redeem(uint amount) public onlyOwner {
require(_totalSupply >= amount);
require(balances[owner] >= amount);
_totalSupply -= amount;
balances[owner] -= amount;
Redeem(amount);
}
// 设置参数
function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
// Ensure transparency by hardcoding limit beyond which fees can never be added
require(newBasisPoints < 20);
require(newMaxFee < 50);
basisPointsRate = newBasisPoints;
maximumFee = newMaxFee.mul(10**decimals);
Params(basisPointsRate, maximumFee);
}
// Called when new token are issued
event Issue(uint amount);
// Called when tokens are redeemed
event Redeem(uint amount);
// Called when contract is deprecated
event Deprecate(address newAddress);
// Called if contract ever adds fees
event Params(uint feeBasisPoints, uint maxFee);
}