Openzeppelin库 07.TOKEN_ERC20

1. ERC20

  • 以太坊TOKEN的标准接口
  • 作用:为了让以太坊上的各类token合约有一个特征与接口的共同标准

2. ERC20.sol:ERC20合约的扩展


pragma solidity ^0.4.24;
 
import "./ERC20Basic.sol";
 
 
/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
contract ERC20 is ERC20Basic {
  function allowance(address _owner, address _spender)
    public view returns (uint256);
  function transferFrom(address _from, address _to, uint256 _value)
    public returns (bool);
  function approve(address _spender, uint256 _value) public returns (bool);
  event Approval(
    address indexed owner,
    address indexed spender,
    uint256 value
  );
}
 

3. ERC20Basic.sol:ERC20基础合约

pragma solidity ^0.4.24;
 
 
/**
 * @title ERC20Basic
 * @dev Simpler version of ERC20 interface
 * See https://github.com/ethereum/EIPs/issues/179
 */
contract ERC20Basic {
  function totalSupply() public view returns (uint256);
  function balanceOf(address _who) public view returns (uint256);
  function transfer(address _to, uint256 _value) public returns (bool);
  event Transfer(address indexed from, address indexed to, uint256 value);
}

 

3. BasicToken.sol:最简单的ERC20接口实现


pragma solidity ^0.4.24;
 
 
import "./ERC20Basic.sol";
import "../../math/SafeMath.sol";
 
 
/**
 * @title Basic token
 * @dev Basic version of StandardToken, with no allowances.
 */
contract BasicToken is ERC20Basic {
  using SafeMath for uint256;
 
  mapping(address => uint256) internal balances;
 
  uint256 internal totalSupply_;
 
  /**
  * @dev Total number of tokens in existence
  */
  function totalSupply() public view returns (uint256) {
    return totalSupply_;
  }
 
  /**
  * @dev 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;
  }
 
  /**
  * @dev 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];
  }
 
}
 

4. StandardToken.sol:标准token实现

pragma solidity ^0.4.24;
 
import "./BasicToken.sol";
import "./ERC20.sol";
 
 
/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * https://github.com/ethereum/EIPs/issues/20
 * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract StandardToken is ERC20, BasicToken {
  // mapping嵌套
  // 允许内层mapping中的地址从外层mapping中所提取的代币额度
  mapping (address => mapping (address => uint256)) internal allowed;
 
  /**
   * @dev 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
   */
  // 通过from向地址to转账
  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;
  }
 
  /**
   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
   * Beware that changing an allowance with this method brings the risk that someone may use both the old
   * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
   * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   * @param _spender The address which will spend the funds.
   * @param _value The amount of tokens to be spent.
   */
  // 设置地址spender可以从msg.sender上面提取的额度
  function approve(address _spender, uint256 _value) public returns (bool) {
    allowed[msg.sender][_spender] = _value;
    emit Approval(msg.sender, _spender, _value);
    return true;
  }
 
  /**
   * @dev 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.
   */
  // 获取spender可以从owner上提取的额度
  function allowance(
    address _owner,
    address _spender
   )
    public
    view
    returns (uint256)
  {
    return allowed[_owner][_spender];
  }
 
  /**
   * @dev 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)
   * From MonolithDAO Token.sol
   * @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;
  }
 
  /**
   * @dev 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)
   * From MonolithDAO Token.sol
   * @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) { // 减少的差值如果大于原有的额度,则相当于不能再从msg.sender中转账
      allowed[msg.sender][_spender] = 0;
    } else {
      allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
    }
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }
 
}

5. MintableToken.sol:铸币合约


pragma solidity ^0.4.24;
 
import "./StandardToken.sol";
import "../../ownership/Ownable.sol";
 
 
/**
 * @title Mintable token
 * @dev Simple ERC20 Token example, with mintable token creation
 * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol
 */
// 铸币合约
contract MintableToken is StandardToken, Ownable {
  event Mint(address indexed to, uint256 amount);
  event MintFinished();
 
  // 铸币完成
  bool public mintingFinished = false;
 
 
  modifier canMint() {
    require(!mintingFinished);
    _;
  }
  // 判断当前地址是否拥有铸币权限
  modifier hasMintPermission() {
    require(msg.sender == owner);
    _;
  }
 
  /**
   * @dev Function to mint tokens
   * @param _to The address that will receive the minted tokens.
   * @param _amount The amount of tokens to mint.
   * @return A boolean that indicates if the operation was successful.
   */
  function mint(
    address _to,
    uint256 _amount
  )
    public
    hasMintPermission
    canMint
    returns (bool)
  {
    // 发行总量要增加
    totalSupply_ = totalSupply_.add(_amount);
    // 指定地址的余额也要增加
    balances[_to] = balances[_to].add(_amount);
 
    emit Mint(_to, _amount);
    // address(0)代表是系统所转
    emit Transfer(address(0), _to, _amount);
    return true;
  }
 
  /**
   * @dev Function to stop minting new tokens.
   * @return True if the operation was successful.
   */
  // 铸币完成
  function finishMinting() public onlyOwner canMint returns (bool) {
    mintingFinished = true;
    emit MintFinished();
    return true;
  }
}
 

6. 铸币权限控制


pragma solidity ^0.4.24;
 
import "./MintableToken.sol";
import "../../access/rbac/RBAC.sol";
 
 
/**
 * @title RBACMintableToken
 * @author Vittorio Minacori (@vittominacori)
 * @dev Mintable Token, with RBAC minter permissions
 */
// 铸币权管理
contract RBACMintableToken is MintableToken, RBAC {
  /**
   * A constant role name for indicating minters.
   */
  // 铸造者角色,拥有铸币权
  string public constant ROLE_MINTER = "minter";
 
  /**
   * @dev override the Mintable token modifier to add role based logic
   */
  // 检查当前地址是否拥有铸币权
  modifier hasMintPermission() {
    checkRole(msg.sender, ROLE_MINTER);
    _;
  }
 
  /**
   * @dev add a minter role to an address
   * @param _minter address
   */
  // 添加铸币权
  function addMinter(address _minter) public onlyOwner {
    addRole(_minter, ROLE_MINTER);
  }
 
  /**
   * @dev remove a minter role from an address
   * @param _minter address
   */
  // 移除铸币权
  function removeMinter(address _minter) public onlyOwner {
    removeRole(_minter, ROLE_MINTER);
  }
}

7. cappedToken.sol:铸币上限


pragma solidity ^0.4.24;
 
import "./MintableToken.sol";
 
 
/**
 * @title Capped token
 * @dev Mintable token with a token cap.
 */
// 设置铸币的上限值
contract CappedToken is MintableToken {
 
  uint256 public cap;
 
  constructor(uint256 _cap) public {
    require(_cap > 0);
    cap = _cap;
  }
 
  /**
   * @dev Function to mint tokens
   * @param _to The address that will receive the minted tokens.
   * @param _amount The amount of tokens to mint.
   * @return A boolean that indicates if the operation was successful.
   */
  function mint(
    address _to,
    uint256 _amount
  )
    public
    returns (bool)
  {
    require(totalSupply_.add(_amount) <= cap);
 
    return super.mint(_to, _amount);
  }

}
 

8. DetailERC20.sol:token详情


pragma solidity ^0.4.24;
 
import "./ERC20.sol";
 
 
/**
 * @title DetailedERC20 token
 * @dev The decimals are only for visualization purposes.
 * All the operations are done using the smallest and indivisible token unit,
 * just as on Ethereum all the operations are done in wei.
 */
contract DetailedERC20 is ERC20 {
  string public name;
  string public symbol;
  uint8 public decimals;
 
  constructor(string _name, string _symbol, uint8 _decimals) public {
    name = _name;
    symbol = _symbol;
    decimals = _decimals;
  }
}

9. PauseableToken.sol:暂停


pragma solidity ^0.4.24;
 
import "./StandardToken.sol";
import "../../lifecycle/Pausable.sol";
 
 
/**
 * @title Pausable token
 * @dev StandardToken modified with pausable transfers.
 **/
// 暂停合约
contract PausableToken is StandardToken, Pausable {
 
  function transfer(
    address _to,
    uint256 _value
  )
    public
    whenNotPaused
    returns (bool)
  {
    return super.transfer(_to, _value);
  }
 
  function transferFrom(
    address _from,
    address _to,
    uint256 _value
  )
    public
    whenNotPaused
    returns (bool)
  {
    return super.transferFrom(_from, _to, _value);
  }
 
  function approve(
    address _spender,
    uint256 _value
  )
    public
    whenNotPaused
    returns (bool)
  {
    return super.approve(_spender, _value);
  }
 
  function increaseApproval(
    address _spender,
    uint _addedValue
  )
    public
    whenNotPaused
    returns (bool success)
  {
    return super.increaseApproval(_spender, _addedValue);
  }
 
  function decreaseApproval(
    address _spender,
    uint _subtractedValue
  )
    public
    whenNotPaused
    returns (bool success)
  {
    return super.decreaseApproval(_spender, _subtractedValue);
  }
}
 

10. StandardBurnableToken:标准token的销毁


pragma solidity ^0.4.24;
 
import "./BurnableToken.sol";
import "./StandardToken.sol";
 
 
/**
 * @title Standard Burnable Token
 * @dev Adds burnFrom method to ERC20 implementations
 */
contract StandardBurnableToken is BurnableToken, StandardToken {
 
  /**
   * @dev Burns a specific amount of tokens from the target address and decrements allowance
   * @param _from address The address which you want to send tokens from
   * @param _value uint256 The amount of token to be burned
   */
  function burnFrom(address _from, uint256 _value) public {
    require(_value <= allowed[_from][msg.sender]);
    // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted,
    // this function needs to emit an event with the updated approval.
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    _burn(_from, _value);
  }
}
 

11. TokenTimeLock.sol:锁定


pragma solidity ^0.4.24;
 
import "./SafeERC20.sol";
 
 
/**
 * @title TokenTimelock
 * @dev TokenTimelock is a token holder contract that will allow a
 * beneficiary to extract the tokens after a given release time
 */
// token锁定合约,在规定时间内锁定token,只有在锁定时间外才能提取代币
contract TokenTimelock {
  using SafeERC20 for ERC20Basic;
 
  // ERC20 basic token contract being held
  ERC20Basic public token;
 
  // beneficiary of tokens after they are released
  address public beneficiary; // 受益人
 
  // timestamp when token release is enabled
  uint256 public releaseTime; // 释放时间
 
  constructor(
    ERC20Basic _token,
    address _beneficiary,
    uint256 _releaseTime
  )
    public
  {
    // solium-disable-next-line security/no-block-members
    require(_releaseTime > block.timestamp);
    token = _token;
    beneficiary = _beneficiary;
    releaseTime = _releaseTime;
  }
 
  /**
   * @notice Transfers tokens held by timelock to beneficiary.
   */
  // 释放
  function release() public {
    // solium-disable-next-line security/no-block-members
    require(block.timestamp >= releaseTime);
 
    uint256 amount = token.balanceOf(address(this));
    require(amount > 0);
 
    token.safeTransfer(beneficiary, amount);
  }
}
 

12. TokenTimeLock.sol:锁定

pragma solidity ^0.4.24;
 
import "./SafeERC20.sol";
 
 
/**
 * @title TokenTimelock
 * @dev TokenTimelock is a token holder contract that will allow a
 * beneficiary to extract the tokens after a given release time
 */
// token锁定合约,在规定时间内锁定token,只有在锁定时间外才能提取代币
contract TokenTimelock {
  using SafeERC20 for ERC20Basic;
 
  // ERC20 basic token contract being held
  ERC20Basic public token;
 
  // beneficiary of tokens after they are released
  address public beneficiary; // 受益人
 
  // timestamp when token release is enabled
  uint256 public releaseTime; // 释放时间
 
  constructor(
    ERC20Basic _token,
    address _beneficiary,
    uint256 _releaseTime
  )
    public
  {
    // solium-disable-next-line security/no-block-members
    require(_releaseTime > block.timestamp);
    token = _token;
    beneficiary = _beneficiary;
    releaseTime = _releaseTime;
  }
 
  /**
   * @notice Transfers tokens held by timelock to beneficiary.
   */
  // 释放
  function release() public {
    // solium-disable-next-line security/no-block-members
    require(block.timestamp >= releaseTime);
 
    uint256 amount = token.balanceOf(address(this));
    require(amount > 0);
 
    token.safeTransfer(beneficiary, amount);
  }

 

13. TokenTimeLock.sol:锁定

pragma solidity ^0.4.24;
 
import "./SafeERC20.sol";
 
 
/**
 * @title TokenTimelock
 * @dev TokenTimelock is a token holder contract that will allow a
 * beneficiary to extract the tokens after a given release time
 */
// token锁定合约,在规定时间内锁定token,只有在锁定时间外才能提取代币
contract TokenTimelock {
  using SafeERC20 for ERC20Basic;
 
  // ERC20 basic token contract being held
  ERC20Basic public token;
 
  // beneficiary of tokens after they are released
  address public beneficiary; // 受益人
 
  // timestamp when token release is enabled
  uint256 public releaseTime; // 释放时间
 
  constructor(
    ERC20Basic _token,
    address _beneficiary,
    uint256 _releaseTime
  )
    public
  {
    // solium-disable-next-line security/no-block-members
    require(_releaseTime > block.timestamp);
    token = _token;
    beneficiary = _beneficiary;
    releaseTime = _releaseTime;
  }
 
  /**
   * @notice Transfers tokens held by timelock to beneficiary.
   */
  // 释放
  function release() public {
    // solium-disable-next-line security/no-block-members
    require(block.timestamp >= releaseTime);
 
    uint256 amount = token.balanceOf(address(this));
    require(amount > 0);
 
    token.safeTransfer(beneficiary, amount);
  }
}

14. TokenVestion.sol:释放

/* solium-disable security/no-block-members */
 
pragma solidity ^0.4.24;
 
import "./ERC20Basic.sol";
import "./SafeERC20.sol";
import "../../ownership/Ownable.sol";
import "../../math/SafeMath.sol";
 
 
/**
 * @title TokenVesting
 * @dev A token holder contract that can release its token balance gradually like a
 * typical vesting scheme, with a cliff and vesting period. Optionally revocable by the
 * owner.
 */
// 定期释放
contract TokenVesting is Ownable {
  using SafeMath for uint256;
  using SafeERC20 for ERC20Basic;
 
  event Released(uint256 amount);
  event Revoked();
 
  // beneficiary of tokens after they are released
  address public beneficiary;
 
  uint256 public cliff;
  uint256 public start;
  uint256 public duration;
 
  bool public revocable;
 
  mapping (address => uint256) public released;
  mapping (address => bool) public revoked;
 
  /**
   * @dev Creates a vesting contract that vests its balance of any ERC20 token to the
   * _beneficiary, gradually in a linear fashion until _start + _duration. By then all
   * of the balance will have vested.
   * @param _beneficiary address of the beneficiary to whom vested tokens are transferred
   * @param _cliff duration in seconds of the cliff in which tokens will begin to vest
   * @param _start the time (as Unix time) at which point vesting starts
   * @param _duration duration in seconds of the period in which the tokens will vest
   * @param _revocable whether the vesting is revocable or not
   */
  constructor(
    address _beneficiary,
    uint256 _start,
    uint256 _cliff,
    uint256 _duration,
    bool _revocable
  )
    public
  {
    require(_beneficiary != address(0));
    require(_cliff <= _duration);
 
    beneficiary = _beneficiary;
    revocable = _revocable;
    duration = _duration;
    cliff = _start.add(_cliff);
    start = _start;
  }
 
  /**
   * @notice Transfers vested tokens to beneficiary.
   * @param _token ERC20 token which is being vested
   */
  // 释放token
  function release(ERC20Basic _token) public {
    uint256 unreleased = releasableAmount(_token);
 
    require(unreleased > 0); // 确保未释放的token大于0
 
    released[_token] = released[_token].add(unreleased);
 
    _token.safeTransfer(beneficiary, unreleased);
 
    emit Released(unreleased);
  }
 
  /**
   * @notice Allows the owner to revoke the vesting. Tokens already vested
   * remain in the contract, the rest are returned to the owner.
   * @param _token ERC20 token which is being vested
   */
  function revoke(ERC20Basic _token) public onlyOwner {
    require(revocable);
    require(!revoked[_token]);
 
    uint256 balance = _token.balanceOf(address(this));
 
    uint256 unreleased = releasableAmount(_token);
    uint256 refund = balance.sub(unreleased);
 
    revoked[_token] = true;
 
    _token.safeTransfer(owner, refund);
 
    emit Revoked();
  }
 
  /**
   * @dev Calculates the amount that has already vested but hasn't been released yet.
   * @param _token ERC20 token which is being vested
   */
  function releasableAmount(ERC20Basic _token) public view returns (uint256) {
    return vestedAmount(_token).sub(released[_token]);
  }
 
  /**
   * @dev Calculates the amount that has already vested.
   * @param _token ERC20 token which is being vested
   */
  // 释放数量
  function vestedAmount(ERC20Basic _token) public view returns (uint256) {
    uint256 currentBalance = _token.balanceOf(address(this));
    uint256 totalBalance = currentBalance.add(released[_token]);
 
    if (block.timestamp < cliff) {
      return 0;
    } else if (block.timestamp >= start.add(duration) || revoked[_token]) {
      return totalBalance;
    } else {
      return totalBalance.mul(block.timestamp.sub(start)).div(duration);
    }
  }
}

15.

猜你喜欢

转载自blog.csdn.net/boss2967/article/details/85105379
今日推荐