[solidity知名合约]ERC20-USDT

925 阅读6分钟

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);
}

源码

etherscan.io/token/0xdac…