幣乎合約solidity源碼解讀

幣乎是什么

幣乎(bihu.com)是代幣驅(qū)動(dòng)的代幣投資者垂直社區(qū)。在幣乎,用戶的付出和貢獻(xiàn)將獲得
相應(yīng)的回報(bào)。幣乎將引入幣乎 ID,以實(shí)現(xiàn)平臺(tái)的透明化運(yùn)作。KEY 是幣乎的區(qū)塊鏈代幣,代
表幣乎及其周邊生態(tài)的使用權(quán)。

本文要解讀的幣乎合約是幣乎基于ERC20標(biāo)準(zhǔn)發(fā)布的代幣。

合約和白皮書

幣乎合約類圖

DSTocken.plantuml.png

合約詳解

幣乎合約實(shí)在ERC20標(biāo)準(zhǔn)下在以太坊發(fā)布的一款代幣。類似的代幣還有EOS代幣等。
ERC20標(biāo)準(zhǔn)很簡(jiǎn)單,主要是定義了8個(gè)函數(shù)和3個(gè)屬性。詳情可以查看ERC20合約。幣乎合約派生至ERC20合約。
DSToken合約是幣乎代幣的主體合約。我們可以從這個(gè)合約的方法往上捋其實(shí)現(xiàn)和其超類的實(shí)現(xiàn)。

合約定義

// 繼承自超類DSTokenBase和DSStop
//DSTokenBase(0)意思是用0為參數(shù)(總供給)構(gòu)造DSTokenBase類
contract DSToken is DSTokenBase(0), DSStop
  • DSStop超類主要定義了兩個(gè)函數(shù)和一個(gè)修改器
    • start():將合約置于啟動(dòng)狀態(tài)。
    • stop():將合約置于停止?fàn)顟B(tài)。
    • modifyer stoppable():檢查合約狀態(tài),如果處于停止?fàn)顟B(tài),將攔截所有定義了stoppable修改器的函數(shù)調(diào)用。
  • DSTokenBase超類派生至ERC20合約,實(shí)現(xiàn)了所有ERC20的方法和屬性

DSToken:構(gòu)造合約

    function DSToken(bytes32 symbol_) {
        // 保存知乎KEY的代幣符號(hào)。ERC20標(biāo)準(zhǔn)屬性。
        symbol = symbol_;
        // 將合約的發(fā)送者地址保存為創(chuàng)建者。ERC20標(biāo)準(zhǔn)屬性。
        generator=msg.sender;
    }

transfer:轉(zhuǎn)賬

    // ERC20標(biāo)準(zhǔn)接口。使用了兩個(gè)修改器stoppable和note
    function transfer(address dst, uint wad) stoppable note returns (bool) {
        // 將參數(shù)透?jìng)鹘o超類DSTokenBase
        return super.transfer(dst, wad);
    }

stoppable修改器

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

require和assert的區(qū)別:

  • require為假時(shí),會(huì)回滾狀態(tài)改變
  • assert為假時(shí),不會(huì)回滾狀態(tài)改變

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 {
        // 獲取調(diào)用合約的第一個(gè)參數(shù)。長(zhǎng)度32字節(jié)。跳過前4字節(jié)是因?yàn)榍?字節(jié)是函數(shù)選擇器
        foo := calldataload(4)
        // 獲取調(diào)用合約的第二個(gè)參數(shù)。長(zhǎng)度32字節(jié)。
        bar := calldataload(36)
        }
        // 發(fā)送LogNote消息
        LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);

        _;
    }

transfer在DSTokenBase中的實(shí)現(xiàn)

    function transfer(address dst, uint wad) returns (bool) {
        // 斷言發(fā)送者代幣余額
        assert(_balances[msg.sender] >= wad);

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

        // 發(fā)送Transfer消息。ERC20標(biāo)準(zhǔn)接口。
        Transfer(msg.sender, dst, wad);

        return true;
    }
  • Solidity合約在轉(zhuǎn)賬時(shí)建議采用如下順序,避免重入導(dǎo)致代幣已經(jīng)轉(zhuǎn)出余額不夠扣的情況
    • 檢查:檢查余額,發(fā)送者,接收者等
    • 扣款:先扣除發(fā)送者的余額
    • 打款:增加接收者的余額

transferFrom從他人處轉(zhuǎn)賬

    // ERC20標(biāo)準(zhǔn)接口
    function transferFrom(
    address src, address dst, uint wad
    ) stoppable note returns (bool) {
        // 調(diào)用超類DSTokenBase
        return super.transferFrom(src, dst, wad);
    }

超類實(shí)現(xiàn)

    function transferFrom(address src, address dst, uint wad) returns (bool) {
        assert(_balances[src] >= wad);
        // 斷言支付方對(duì)轉(zhuǎn)賬發(fā)起方的授權(quán)余額
        assert(_approvals[src][msg.sender] >= wad);

        // 扣除支付方對(duì)轉(zhuǎn)賬發(fā)起方的授權(quán)余額
        _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:給別人轉(zhuǎn)賬

這其實(shí)是transfer的別名。

pull:把別人的代幣轉(zhuǎn)給我

    function pull(address src, uint128 wad) returns (bool) {
        // 將src的錢轉(zhuǎn)給合約調(diào)用者,前提是src對(duì)調(diào)用者有足額授權(quán)
        return transferFrom(src, msg.sender, wad);
    }

mint:造幣

    // 只有合約創(chuàng)建者,被認(rèn)證者可以造幣。
    function mint(uint128 wad) auth stoppable note {
        // 給合約調(diào)用者加代幣
        _balances[msg.sender] = add(_balances[msg.sender], wad);
        // 增加總供給
        _supply = add(_supply, wad);
    }

auth修改器

DSAuthEvents合約中的實(shí)現(xiàn)

    modifier auth {
        // 斷言
        assert(isAuthorized(msg.sender, msg.sig));
        _;
    }
    
    function isAuthorized(address src, bytes4 sig) internal returns (bool) {
        if (src == address(this)) {
            // 如果是合約自己調(diào)用該函數(shù),返回true
            return true;
        } else if (src == owner) {
            // 如果是合約的創(chuàng)建者,返回true
            return true;
        } else if (authority == DSAuthority(0)) {
            // 如果authority沒有被設(shè)置(沒人調(diào)用過setAuthority(),該值為初始0值)
            return false;
        } else {
            // 如果authority被設(shè)置過,調(diào)用authority合約判斷授權(quán)
            return authority.canCall(src, this, sig);
        }
    }

burn:燒錢

這是幣乎比較有意思的地方。根據(jù)幣乎的白皮書,打廣告等行為是會(huì)“燒錢”的,人為制造通縮,鼓勵(lì)持有者持幣升值。

    // 只有合約創(chuàng)建者和被認(rèn)證者能燒錢。減少總供給。
    function burn(uint128 wad) auth stoppable note {
        _balances[msg.sender] = sub(_balances[msg.sender], wad);
        _supply = sub(_supply, wad);
    }

generatorTransfer:超級(jí)權(quán)限轉(zhuǎn)賬

    modifier onlyGenerator {
        // 如果不是創(chuàng)建者,回滾狀態(tài),返回剩余gas
        if(msg.sender!=generator) throw;
        _;
    }

    // 合約創(chuàng)建者在任何狀態(tài)下都可以轉(zhuǎn)賬,包括stop狀態(tài)
    function generatorTransfer(address dst, uint wad) onlyGenerator note returns (bool) {
        return super.transfer(dst, wad);
    }

設(shè)置代幣名稱

    // 只有創(chuàng)建者和認(rèn)證用戶可以設(shè)置/修改代幣名稱
    function setName(bytes32 name_) auth {
        // ERC20標(biāo)準(zhǔn)屬性
        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_;
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容