币乎合约solidity源码解读

币乎是什么

币乎(bihu.com)是代币驱动的代币投资者垂直社区。在币乎,用户的付出和贡献将获得
相应的回报。币乎将引入币乎 ID,以实现平台的透明化运作。KEY 是币乎的区块链代币,代
表币乎及其周边生态的使用权。

本文要解读的币乎合约是币乎基于ERC20标准发布的代币。

合约和白皮书

币乎合约类图

DSTocken.plantuml.png

合约详解

币乎合约实在ERC20标准下在以太坊发布的一款代币。类似的代币还有EOS代币等。
ERC20标准很简单,主要是定义了8个函数和3个属性。详情可以查看ERC20合约。币乎合约派生至ERC20合约。
DSToken合约是币乎代币的主体合约。我们可以从这个合约的方法往上捋其实现和其超类的实现。

合约定义

// 继承自超类DSTokenBase和DSStop
//DSTokenBase(0)意思是用0为参数(总供给)构造DSTokenBase类
contract DSToken is DSTokenBase(0), DSStop
  • DSStop超类主要定义了两个函数和一个修改器
    • start():将合约置于启动状态。
    • stop():将合约置于停止状态。
    • modifyer stoppable():检查合约状态,如果处于停止状态,将拦截所有定义了stoppable修改器的函数调用。
  • DSTokenBase超类派生至ERC20合约,实现了所有ERC20的方法和属性

DSToken:构造合约

    function DSToken(bytes32 symbol_) {
        // 保存知乎KEY的代币符号。ERC20标准属性。
        symbol = symbol_;
        // 将合约的发送者地址保存为创建者。ERC20标准属性。
        generator=msg.sender;
    }

transfer:转账

    // ERC20标准接口。使用了两个修改器stoppable和note
    function transfer(address dst, uint wad) stoppable note returns (bool) {
        // 将参数透传给超类DSTokenBase
        return super.transfer(dst, wad);
    }

stoppable修改器

    modifier stoppable {
        // 断言stopped是否为假
        assert (!stopped);
        _;
    }

require和assert的区别:

  • require为假时,会回滚状态改变
  • assert为假时,不会回滚状态改变

note修改器

    event LogNote(
    bytes4   indexed  sig,
    address  indexed  guy,
    bytes32  indexed  foo,
    bytes32  indexed  bar,
    uint        wad,
    bytes             fax
    ) anonymous; //匿名消息(不记录"LogNote")

    modifier note {
        bytes32 foo;
        bytes32 bar;

        // 插入EVM汇编指令
        assembly {
        // 获取调用合约的第一个参数。长度32字节。跳过前4字节是因为前4字节是函数选择器
        foo := calldataload(4)
        // 获取调用合约的第二个参数。长度32字节。
        bar := calldataload(36)
        }
        // 发送LogNote消息
        LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);

        _;
    }

transfer在DSTokenBase中的实现

    function transfer(address dst, uint wad) returns (bool) {
        // 断言发送者代币余额
        assert(_balances[msg.sender] >= wad);

        // 扣除发送者余额
        _balances[msg.sender] = sub(_balances[msg.sender], wad);
        // 增加接收者余额
        _balances[dst] = add(_balances[dst], wad);

        // 发送Transfer消息。ERC20标准接口。
        Transfer(msg.sender, dst, wad);

        return true;
    }
  • Solidity合约在转账时建议采用如下顺序,避免重入导致代币已经转出余额不够扣的情况
    • 检查:检查余额,发送者,接收者等
    • 扣款:先扣除发送者的余额
    • 打款:增加接收者的余额

transferFrom从他人处转账

    // ERC20标准接口
    function transferFrom(
    address src, address dst, uint wad
    ) stoppable note returns (bool) {
        // 调用超类DSTokenBase
        return super.transferFrom(src, dst, wad);
    }

超类实现

    function transferFrom(address src, address dst, uint wad) returns (bool) {
        assert(_balances[src] >= wad);
        // 断言支付方对转账发起方的授权余额
        assert(_approvals[src][msg.sender] >= wad);

        // 扣除支付方对转账发起方的授权余额
        _approvals[src][msg.sender] = sub(_approvals[src][msg.sender], wad);
        _balances[src] = sub(_balances[src], wad);
        _balances[dst] = add(_balances[dst], wad);

        Transfer(src, dst, wad);

        return true;
    }

push:给别人转账

这其实是transfer的别名。

pull:把别人的代币转给我

    function pull(address src, uint128 wad) returns (bool) {
        // 将src的钱转给合约调用者,前提是src对调用者有足额授权
        return transferFrom(src, msg.sender, wad);
    }

mint:造币

    // 只有合约创建者,被认证者可以造币。
    function mint(uint128 wad) auth stoppable note {
        // 给合约调用者加代币
        _balances[msg.sender] = add(_balances[msg.sender], wad);
        // 增加总供给
        _supply = add(_supply, wad);
    }

auth修改器

DSAuthEvents合约中的实现

    modifier auth {
        // 断言
        assert(isAuthorized(msg.sender, msg.sig));
        _;
    }
    
    function isAuthorized(address src, bytes4 sig) internal returns (bool) {
        if (src == address(this)) {
            // 如果是合约自己调用该函数,返回true
            return true;
        } else if (src == owner) {
            // 如果是合约的创建者,返回true
            return true;
        } else if (authority == DSAuthority(0)) {
            // 如果authority没有被设置(没人调用过setAuthority(),该值为初始0值)
            return false;
        } else {
            // 如果authority被设置过,调用authority合约判断授权
            return authority.canCall(src, this, sig);
        }
    }

burn:烧钱

这是币乎比较有意思的地方。根据币乎的白皮书,打广告等行为是会“烧钱”的,人为制造通缩,鼓励持有者持币升值。

    // 只有合约创建者和被认证者能烧钱。减少总供给。
    function burn(uint128 wad) auth stoppable note {
        _balances[msg.sender] = sub(_balances[msg.sender], wad);
        _supply = sub(_supply, wad);
    }

generatorTransfer:超级权限转账

    modifier onlyGenerator {
        // 如果不是创建者,回滚状态,返回剩余gas
        if(msg.sender!=generator) throw;
        _;
    }

    // 合约创建者在任何状态下都可以转账,包括stop状态
    function generatorTransfer(address dst, uint wad) onlyGenerator note returns (bool) {
        return super.transfer(dst, wad);
    }

设置代币名称

    // 只有创建者和认证用户可以设置/修改代币名称
    function setName(bytes32 name_) auth {
        // ERC20标准属性
        name = name_;
    }

币乎合约全文

// Copyright (C) 2017 DappHub, LLC

pragma solidity ^0.4.11;

//import "ds-exec/exec.sol";

contract DSExec {
    function tryExec( address target, bytes calldata, uint value)
    internal
    returns (bool call_ret)
    {
        return target.call.value(value)(calldata);
    }
    function exec( address target, bytes calldata, uint value)
    internal
    {
        if(!tryExec(target, calldata, value)) {
            throw;
        }
    }

    // Convenience aliases
    function exec( address t, bytes c )
    internal
    {
        exec(t, c, 0);
    }
    function exec( address t, uint256 v )
    internal
    {
        bytes memory c; exec(t, c, v);
    }
    function tryExec( address t, bytes c )
    internal
    returns (bool)
    {
        return tryExec(t, c, 0);
    }
    function tryExec( address t, uint256 v )
    internal
    returns (bool)
    {
        bytes memory c; return tryExec(t, c, v);
    }
}

//import "ds-auth/auth.sol";
contract DSAuthority {
    function canCall(
    address src, address dst, bytes4 sig
    ) constant returns (bool);
}

contract DSAuthEvents {
    event LogSetAuthority (address indexed authority);
    event LogSetOwner     (address indexed owner);
}

contract DSAuth is DSAuthEvents {
    DSAuthority  public  authority;
    address      public  owner;

    function DSAuth() {
        owner = msg.sender;
        LogSetOwner(msg.sender);
    }

    function setOwner(address owner_)
    auth
    {
        owner = owner_;
        LogSetOwner(owner);
    }

    function setAuthority(DSAuthority authority_)
    auth
    {
        authority = authority_;
        LogSetAuthority(authority);
    }

    modifier auth {
        assert(isAuthorized(msg.sender, msg.sig));
        _;
    }

    function isAuthorized(address src, bytes4 sig) internal returns (bool) {
        if (src == address(this)) {
            return true;
        } else if (src == owner) {
            return true;
        } else if (authority == DSAuthority(0)) {
            return false;
        } else {
            return authority.canCall(src, this, sig);
        }
    }

    function assert(bool x) internal {
        if (!x) throw;
    }
}

//import "ds-note/note.sol";
contract DSNote {
    event LogNote(
    bytes4   indexed  sig,
    address  indexed  guy,
    bytes32  indexed  foo,
    bytes32  indexed  bar,
    uint        wad,
    bytes             fax
    ) anonymous;

    modifier note {
        bytes32 foo;
        bytes32 bar;

        assembly {
        foo := calldataload(4)
        bar := calldataload(36)
        }

        LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);

        _;
    }
}


//import "ds-math/math.sol";
contract DSMath {

    /*
    standard uint256 functions
     */

    function add(uint256 x, uint256 y) constant internal returns (uint256 z) {
        assert((z = x + y) >= x);
    }

    function sub(uint256 x, uint256 y) constant internal returns (uint256 z) {
        assert((z = x - y) <= x);
    }

    function mul(uint256 x, uint256 y) constant internal returns (uint256 z) {
        z = x * y;
        assert(x == 0 || z / x == y);
    }

    function div(uint256 x, uint256 y) constant internal returns (uint256 z) {
        z = x / y;
    }

    function min(uint256 x, uint256 y) constant internal returns (uint256 z) {
        return x <= y ? x : y;
    }
    function max(uint256 x, uint256 y) constant internal returns (uint256 z) {
        return x >= y ? x : y;
    }

    /*
    uint128 functions (h is for half)
     */


    function hadd(uint128 x, uint128 y) constant internal returns (uint128 z) {
        assert((z = x + y) >= x);
    }

    function hsub(uint128 x, uint128 y) constant internal returns (uint128 z) {
        assert((z = x - y) <= x);
    }

    function hmul(uint128 x, uint128 y) constant internal returns (uint128 z) {
        z = x * y;
        assert(x == 0 || z / x == y);
    }

    function hdiv(uint128 x, uint128 y) constant internal returns (uint128 z) {
        z = x / y;
    }

    function hmin(uint128 x, uint128 y) constant internal returns (uint128 z) {
        return x <= y ? x : y;
    }
    function hmax(uint128 x, uint128 y) constant internal returns (uint128 z) {
        return x >= y ? x : y;
    }


    /*
    int256 functions
     */

    function imin(int256 x, int256 y) constant internal returns (int256 z) {
        return x <= y ? x : y;
    }
    function imax(int256 x, int256 y) constant internal returns (int256 z) {
        return x >= y ? x : y;
    }

    /*
    WAD math
     */

    uint128 constant WAD = 10 ** 18;

    function wadd(uint128 x, uint128 y) constant internal returns (uint128) {
        return hadd(x, y);
    }

    function wsub(uint128 x, uint128 y) constant internal returns (uint128) {
        return hsub(x, y);
    }

    function wmul(uint128 x, uint128 y) constant internal returns (uint128 z) {
        z = cast((uint256(x) * y + WAD / 2) / WAD);
    }

    function wdiv(uint128 x, uint128 y) constant internal returns (uint128 z) {
        z = cast((uint256(x) * WAD + y / 2) / y);
    }

    function wmin(uint128 x, uint128 y) constant internal returns (uint128) {
        return hmin(x, y);
    }
    function wmax(uint128 x, uint128 y) constant internal returns (uint128) {
        return hmax(x, y);
    }

    /*
    RAY math
     */

    uint128 constant RAY = 10 ** 27;

    function radd(uint128 x, uint128 y) constant internal returns (uint128) {
        return hadd(x, y);
    }

    function rsub(uint128 x, uint128 y) constant internal returns (uint128) {
        return hsub(x, y);
    }

    function rmul(uint128 x, uint128 y) constant internal returns (uint128 z) {
        z = cast((uint256(x) * y + RAY / 2) / RAY);
    }

    function rdiv(uint128 x, uint128 y) constant internal returns (uint128 z) {
        z = cast((uint256(x) * RAY + y / 2) / y);
    }

    function rpow(uint128 x, uint64 n) constant internal returns (uint128 z) {
        // This famous algorithm is called "exponentiation by squaring"
        // and calculates x^n with x as fixed-point and n as regular unsigned.
        //
        // It's O(log n), instead of O(n) for naive repeated multiplication.
        //
        // These facts are why it works:
        //
        //  If n is even, then x^n = (x^2)^(n/2).
        //  If n is odd,  then x^n = x * x^(n-1),
        //   and applying the equation for even x gives
        //    x^n = x * (x^2)^((n-1) / 2).
        //
        //  Also, EVM division is flooring and
        //    floor[(n-1) / 2] = floor[n / 2].

        z = n % 2 != 0 ? x : RAY;

        for (n /= 2; n != 0; n /= 2) {
            x = rmul(x, x);

            if (n % 2 != 0) {
                z = rmul(z, x);
            }
        }
    }

    function rmin(uint128 x, uint128 y) constant internal returns (uint128) {
        return hmin(x, y);
    }
    function rmax(uint128 x, uint128 y) constant internal returns (uint128) {
        return hmax(x, y);
    }

    function cast(uint256 x) constant internal returns (uint128 z) {
        assert((z = uint128(x)) == x);
    }

}

//import "erc20/erc20.sol";
contract ERC20 {
    function totalSupply() constant returns (uint supply);
    function balanceOf( address who ) constant returns (uint value);
    function allowance( address owner, address spender ) constant returns (uint _allowance);

    function transfer( address to, uint value) returns (bool ok);
    function transferFrom( address from, address to, uint value) returns (bool ok);
    function approve( address spender, uint value ) returns (bool ok);

    event Transfer( address indexed from, address indexed to, uint value);
    event Approval( address indexed owner, address indexed spender, uint value);
}



//import "ds-token/base.sol";
contract DSTokenBase is ERC20, DSMath {
    uint256                                            _supply;
    mapping (address => uint256)                       _balances;
    mapping (address => mapping (address => uint256))  _approvals;

    function DSTokenBase(uint256 supply) {
        _balances[msg.sender] = supply;
        _supply = supply;
    }

    function totalSupply() constant returns (uint256) {
        return _supply;
    }
    function balanceOf(address src) constant returns (uint256) {
        return _balances[src];
    }
    function allowance(address src, address guy) constant returns (uint256) {
        return _approvals[src][guy];
    }

    function transfer(address dst, uint wad) returns (bool) {
        assert(_balances[msg.sender] >= wad);

        _balances[msg.sender] = sub(_balances[msg.sender], wad);
        _balances[dst] = add(_balances[dst], wad);

        Transfer(msg.sender, dst, wad);

        return true;
    }

    function transferFrom(address src, address dst, uint wad) returns (bool) {
        assert(_balances[src] >= wad);
        assert(_approvals[src][msg.sender] >= wad);

        _approvals[src][msg.sender] = sub(_approvals[src][msg.sender], wad);
        _balances[src] = sub(_balances[src], wad);
        _balances[dst] = add(_balances[dst], wad);

        Transfer(src, dst, wad);

        return true;
    }

    function approve(address guy, uint256 wad) returns (bool) {
        _approvals[msg.sender][guy] = wad;

        Approval(msg.sender, guy, wad);

        return true;
    }

}


//import "ds-stop/stop.sol";
contract DSStop is DSAuth, DSNote {

    bool public stopped;

    modifier stoppable {
        assert (!stopped);
        _;
    }
    function stop() auth note {
        stopped = true;
    }
    function start() auth note {
        stopped = false;
    }

}


//import "ds-token/token.sol";
contract DSToken is DSTokenBase(0), DSStop {

    bytes32  public  symbol;
    uint256  public  decimals = 18; // standard token precision. override to customize
    address  public  generator;

    modifier onlyGenerator {
        if(msg.sender!=generator) throw;
        _;
    }

    function DSToken(bytes32 symbol_) {
        symbol = symbol_;
        generator=msg.sender;
    }

    function transfer(address dst, uint wad) stoppable note returns (bool) {
        return super.transfer(dst, wad);
    }
    function transferFrom(
    address src, address dst, uint wad
    ) stoppable note returns (bool) {
        return super.transferFrom(src, dst, wad);
    }
    function approve(address guy, uint wad) stoppable note returns (bool) {
        return super.approve(guy, wad);
    }

    function push(address dst, uint128 wad) returns (bool) {
        return transfer(dst, wad);
    }
    function pull(address src, uint128 wad) returns (bool) {
        return transferFrom(src, msg.sender, wad);
    }

    function mint(uint128 wad) auth stoppable note {
        _balances[msg.sender] = add(_balances[msg.sender], wad);
        _supply = add(_supply, wad);
    }
    function burn(uint128 wad) auth stoppable note {
        _balances[msg.sender] = sub(_balances[msg.sender], wad);
        _supply = sub(_supply, wad);
    }

    // owner can transfer token even stop,
    function generatorTransfer(address dst, uint wad) onlyGenerator note returns (bool) {
        return super.transfer(dst, wad);
    }

    // Optional token name

    bytes32   public  name = "";

    function setName(bytes32 name_) auth {
        name = name_;
    }

}

推荐阅读更多精彩内容

  • 阮丽收到周建坤的短信后,立刻返回到茶餐厅,可是当她到达那里时,周健坤已经离开了。阮丽迅速拨通了周建坤的电话,语音提...
    之亦夫阅读 154评论 0 1
  • 周日上午,看着简友们的文章,有一搭没一搭地听着方方弹钢琴。 当看到风静写的《控制与失控》,方方刚好弹起宫崎骏的《天...
    文晓玲阅读 120评论 7 5
  • 2016年,愚人节的前一天晚上,人称“女魔头”的天才建筑师标杆之一 Zaha Hadid过世。圈内震惊,朋友圈被瞬...
    酱爆阅读 621评论 0 0
  • 我写这封信,一定是上天的恩赐。 我老了。 尽量不语无伦次。 我坐在花园里,听见鸟叫声。想起无忧无虑的少年时光,尽情...
    向内的旅程阅读 260评论 0 0