国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

ERC20重要補充之approveAndCall

nanchen2251 / 1047人閱讀

摘要:假設某一天,星巴克突然宣布為了擁抱區塊鏈技術,不再接受法幣買咖啡了,大家以后可以用以太幣或者星巴克自己發行的星星幣來買咖啡。用星星幣買咖啡星巴克自己發行了,取名,遵循協議。

什么是ERC20

ERC20是以太坊上為token提供的一種協議,也可以理解成一種token的共同標準。遵循ERC20協議的token都可以兼容以太坊錢包,讓用戶在錢包中可以查看token余額以及操作token轉賬,而不需要自己再手動與token合約交互。

ERC20規定了以下基本方法:

contract ERC20 {
    // 方法
    function name() view returns (string name);
    function symbol() view returns (string symbol);
    function decimals() view returns (uint8 decimals);
    function totalSupply() view returns (uint256 totalSupply);
    function balanceOf(address _owner) view returns (uint256 balance);
    function transfer(address _to, uint256 _value) returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
    function approve(address _spender, uint256 _value) returns (bool success);
    function allowance(address _owner, address _spender) view returns (uint256 remaining);
    // 事件
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

可以看到,通過上面的幾種方法,規定了一種token的基本信息、轉賬以及授權操作。這些操作基本可以覆蓋貨幣使用的絕大部分場景,該協議一經提出后,立得到了開發者的接納。

ERC20的局限
ERC20雖然廣受開發者喜愛,但是依然有自己局限的一面。

讓我們先從一個大家十分熟悉的場景開始談起。假設某一天,星巴克突然宣布為了擁抱區塊鏈技術,不再接受法幣買咖啡了,大家以后可以用以太幣或者星巴克自己發行的星星幣來買咖啡。

首先,我們來看用以太幣來買咖啡的流程。

1. 用以太幣買咖啡

簡單寫一個買咖啡的合約(注:偽代碼,僅表示邏輯)

contract BuyCoffee {
    function buy() public payable {
        starbucks.transfer(msg.value);
        COFFEE.transfer(msg.sender);
    }
}

(熟悉ERC721的小伙伴肯定看出來了,這里的COFFEE是遵守ERC721的NFT token,本文重點講解的是ERC20,因此就不在贅述ERC721的實現了)。

整個調用過程如下圖:

客戶直接調用buy()方法,輸入買咖啡需要的以太幣數量,BuyCoffee合約就把自己有的COFFEE轉給客戶。整個過程只需要一步。

2. 用星星幣買咖啡

星巴克自己發行了token,取名StarCoin,遵循ERC20協議。

那么BuyCoffee合約就要做一些小修改:(注:偽代碼,僅表示邏輯)

contract BuyCoffee {
    // 一杯咖啡的StarCoin價格
    uint constant COFFEE_PRICE;
    //@param _fee - 用戶買咖啡需要支付的StarCoin數量
    function buy(uint _fee) public payable {
        require(_fee >= COFFEE_PRICE);
        StarCoin.transferFrom(msg.sender, address(this), _fee);
        COFFEE.transfer(msg.sender);
    }
}

整個買咖啡的過程如下圖:

圖中可以看到,因為StarCoinBuyCoffee是兩個合約,分別有自己獨立的地址,所以客戶買咖啡就要經過兩次操作:

先要把買咖啡的starcoin數量授權給BuyCoffee

然后調用BuyCoffee中的buy(uint)方法買咖啡;

3. 以太幣 vs 星星幣

通過上面的分析可以看到,如果要使用星巴克發行的StarCoin進行付款的話,買一杯咖啡要操作兩次,無疑這增加了操作成本,并且很反常識。一個很好的辦法就是把StarCoinBuyCoffee合二為一,如果token邏輯和業務邏輯都在同一個合約里的話,就不存在上述問題了。

這看上去是一個不錯的辦法,然而治標不治本。萬一以后星巴克還宣布可以使用星星幣買積分、參加優惠活動甚至直接參與星巴克公司分紅,鑒于智能合約不可更改的特點,這么多業務邏輯不可能一開始就全部規劃好,以后的新業務依然面臨多次操作的問題。

approveAndCall

approveAndCall方法可以完美地解決上述問題,把兩次操作合并為一次,讓用戶在付款時感覺不到這些復雜的操作。

使用approveAndCall方法之后,整個操作的流程如下:

用戶在token合約 (StarCoin) 中授權一筆token給業務合約 (BuyCoffee), 通過token合約中的approveAndCall方法;

token合約通知業務合約,它已經被授權可以操作用戶的一筆token,通過調用業務合約的receiveApproval方法;

業務合約就可以把用戶的token轉給自己,然后自己再去完成相關的業務邏輯(比如把咖啡轉給用戶,或者自己再做一些轉賬操作)。

整個過程就如下圖:

這就需要在token合約里創建approveAndCall方法,如下:

function approveAndCall(address _to, uint256 _value, bytes _extraData) {
    approve(_to, _value);
    ApproveAndCallFallBack(_to).receiveApproval(
        msg.sender,
        _value,
        extraData)
}

(參數的個數可以根據需要自行選擇,例如可以加上address(tokenContract))

然后在service合約中創建receiveApproval方法,如下:

function receiveApproval(address _sender, uint256 _value, bytes _extraData) {
    require(msg.sender == tokenContract);
    // do something by breaking down _extraData
    ...
}
approveAndCall使用注意事項

為什么要使用approveAndCall以及怎樣使用它,上文已經解釋清楚了。有些可能覺得再多寫一個ApproveAndCallFallBack接口有些多此一舉,不如直接使用address(_to).call(...)來的簡單直接。

ConsenSys的疏忽

ConsenSys公司的思路也是這樣的,以下代碼就是Consensys的approveAndCall方法:

  /* Approves and then calls the receiving contract */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);

        //call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn"t have to include a contract in here just for this.
        //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
        //it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead.
        if(!_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { throw; }
        return true;
    }
}
想看全部源碼的可以訪問:https://github.com/ConsenSys/...

但是大家如果稍加嘗試就會發現,如果這里的_extraData超過32個字節,就會報錯。

原因就在于address(_to).call(...)這樣的調用,并不會對所傳數據做ABI.encode編碼,而bytes作為動態數據類型,它的ABI編碼方式和基礎的、固定長度類型的變量是不一樣的。

舉個例子:

下面是長度為64字節的bytes (換行只是為了讓大家看著不費力) :

0x0000000000000000000000000000000100000000000000000000000000000001
  000000000000000000000000964633feef5a290be634c2e718353b98def350be

它的ABI編碼如下 (換行只是為了讓大家看著不費力) :

0x0000000000000000000000000000000100000000000000000000000000000060
  0000000000000000000000000000000100000000000000000000000000000040
  0000000000000000000000000000000100000000000000000000000000000001
  000000000000000000000000964633feef5a290be634c2e718353b98def350be

第一行(第一個32byte):距離參數開始位置的偏移量;

第二行(第二個32byte):bytes參數的長度;

第三行和第四行(最后64個byte):bytes參數的內容;

所以上面的bytes參數如果超過32byte長度,第二個32byte就會被當成bytes參數的長度,最后因為out of gas而導致調用失敗。

以上錯誤的修復方式

針對上面的ConsenSys公司的代碼,正確寫法應該是:

/* Approves and then calls the receiving contract */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
        approve(_spender, _value); //如果該token遵循ERC20的話
             if(!_spender.call(bytes4(keccak256("receiveApproval(address,uint256,address,bytes)")), abi.encode(msg.sender, _value, this, _extraData)) { throw; }
        return true;
    }
}

address(_spender).call(...)方法中,使用abi.encode()方法對參數進行ABI編碼,可以防止出現上述錯誤。

approveAndCall的正確打開方式

接著上面的代碼繼續說,除了上面的abi.encode對參數進行ABI編碼的例子,還可以使用abi.encodeWithSelector(...)方法:

/* Approves and then calls the receiving contract */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
        approve(_spender, _value); //如果該token遵循ERC20的話
             if(!_spender.call(abi.encodeWithSelector(bytes4(keccak256("receiveApproval(address,uint256,address,bytes)")),msg.sender, _value, this, _extraData)) { throw; }
        return true;
    }
}

abi.encodeWithSelector會自動忽略前四個字節,對后面的內容進行ABI編碼。

還有一個使代碼看上去更加簡潔的代碼方式就是上面提到的,增加ApproveAndCallFallBack接口:

interface ApproveAndCallFallBack {
    function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
}

之后approveAndCall方法內的實現變為:

function approveAndCall(address _spender, uint256 _amount, bytes _extraData
    ) returns (bool success) {
        if (!approve(_spender, _amount)) throw;

        ApproveAndCallFallBack(_spender).receiveApproval(
            msg.sender,
            _amount,
            this,
            _extraData
        );

        return true;
    }
以上代碼貢獻自:

https://github.com/evolutionl...

注:這是一個以太坊上的沙盤游戲。其中RING token的設計目的之一就是為了在游戲中買賣地塊,感興趣的同學可以詳細研究其中的erc20和erc721token之間的交互方式。

寫在最后

這一篇解釋了為什么使用approveAndCall以及怎樣更好地使用它。區塊鏈是一個更新迭代迅速同時又極其強調安全的領域,對于權威組織給出的代碼,我們也不能簡單地copy-and-paste,審計和測試是必須的。

至于ERC20為什么沒有把approveAndCall添加進協議中,可能早期在以太坊上流通的大部分多為token合約,還沒有能夠建立去較為復雜的應用強的程序,因此更加強調的是token作為貨幣具有的流通手段的職能;隨著以太坊生態的發展出現了越來越多的應用,這時ERC20 token的支付手段的職能才被大家重視起來。

也可能因為approveAndCall和業務的聯系過于緊密,ERC20作為一個框架性的協議,這些細節并不在考慮范圍之內。

鑒于智能合約的不可更改性,希望今后的發行token的組織機構或者個人,在實現ERC20的基礎上,可以盡可能安全地實現approveAndCall方法,使得基于token的應用生態更加魯棒。

最后提醒,ERC223的tokenFallback方法也有類似的效果,如果大家感興趣也可以自己做進一步的研究。

友情提醒:ERC223的tokenFallback方法在之前提到的https://github.com/evolutionl...,感興趣的朋友可以自行參考。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/24189.html

相關文章

  • 剖析非同質化代幣ERC721-全面解析ERC721標準

    摘要:本文就來剖析下什么是是什么在創建代幣一篇,我們講到過代幣,和一樣,同樣是一個代幣標準,官方簡要解釋是,簡寫為,多翻譯為非同質代幣。返回合約代幣符號,盡管是可選,但強烈建議實現,即便是返回空字符串。 本文首發于深入淺出區塊鏈社區原文鏈接:剖析非同質化代幣ERC721-全面解析ERC721標準原文已更新,請讀者前往原文閱讀 什么是ERC-721?現在我們看到的各種加密貓貓狗狗都是基于ERC...

    Sike 評論0 收藏0
  • 以太坊開發實戰學習-ERC721標準(七)

    摘要:從這節開始,我們將學習代幣標準以及加密收集資產等知識。聲明一個繼承的新合約,命名為。注意目前是一個草稿,還沒有正式商定的實現。所以把這一個可能的實現當作考慮,但不要把它作為代幣的官方標準。 從這節開始,我們將學習代幣, ERC721標準, 以及加密收集資產等知識。 一、代幣 代幣 讓我們來聊聊以太坊上的代幣。 如果你對以太坊的世界有一些了解,你很可能聽過人們聊到代幣——尤其是 ERC2...

    android_c 評論0 收藏0
  • 以太坊智能合約批量轉幣

    摘要:用途我們為什么需要批量轉幣這樣的智能合約呢大大節約轉幣的資金成本。但是使用這個批量轉幣的智能合約,一般來說,兩百次左右可以一次性操作完,那么也就是兩百次轉幣費只需要支付一次轉幣費即可。大大節約轉幣的人工成本。 一直想寫這篇教程來著,因為你會發現網絡上很少有關于批量轉幣的詳盡的教程,一些提供該工具的網站也并不會將其智能合約代碼開源出來。雖然最終我們會發現原來這個批量轉幣的智能合約原來就這...

    sPeng 評論0 收藏0
  • 以太坊標準令牌系列同質化令牌ERC20

    摘要:目前市面上,凡是基于以太坊的令牌,在交易所上線交易的均是令牌,那么今天我們就來聊聊令牌的標準方案吧。 0x00 寫在前面 眾所周知,以太坊在現階段最大的應用就是令牌發行,而在以太坊中有很多類型的令牌,最著名的當屬ERC20了,但是對于其他幾種令牌類型,可能還有一些朋友不知道,所以最近規劃了一個系列,就是以太坊標準令牌系列。 目前市面上,凡是基于以太坊的令牌,在交易所上線交易的均是ERC...

    Little_XM 評論0 收藏0
  • OpenZeppelin ERC20源碼分析

    摘要:前提是擁有者必須要通過某些機制對這個請求進行確認,比如通過進行。事件,當被調用時,需要觸發該事件。允許從中轉出的數增加所有者允許花費代幣的數量。已經歸屬合約,其余歸還給所有者。計算已歸屬但尚未釋放的金額。源碼分析到這里就結束了。 ERC20:Ethereum Request for Comments 20,是一個基于以太坊代幣的接口標準(協議)。所有符合ERC-20標準的代幣都能立即兼...

    kumfo 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<