成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ERC20重要補充之a(chǎn)pproveAndCall

nanchen2251 / 1044人閱讀

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

什么是ERC20

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

ERC20規(guī)定了以下基本方法:

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

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

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

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

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

1. 用以太幣買咖啡

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

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

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

整個調(diào)用過程如下圖:

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

2. 用星星幣買咖啡

星巴克自己發(fā)行了token,取名StarCoin,遵循ERC20協(xié)議。

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

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

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

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

先要把買咖啡的starcoin數(shù)量授權(quán)給BuyCoffee;

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

3. 以太幣 vs 星星幣

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

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

approveAndCall

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

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

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

token合約通知業(yè)務合約,它已經(jīng)被授權(quán)可以操作用戶的一筆token,通過調(diào)用業(yè)務合約的receiveApproval方法;

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

整個過程就如下圖:

這就需要在token合約里創(chuàng)建approveAndCall方法,如下:

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

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

然后在service合約中創(chuàng)建receiveApproval方法,如下:

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

為什么要使用approveAndCall以及怎樣使用它,上文已經(jīng)解釋清楚了。有些可能覺得再多寫一個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/...

但是大家如果稍加嘗試就會發(fā)現(xiàn),如果這里的_extraData超過32個字節(jié),就會報錯。

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

舉個例子:

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

0x0000000000000000000000000000000100000000000000000000000000000001
  000000000000000000000000964633feef5a290be634c2e718353b98def350be

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

0x0000000000000000000000000000000100000000000000000000000000000060
  0000000000000000000000000000000100000000000000000000000000000040
  0000000000000000000000000000000100000000000000000000000000000001
  000000000000000000000000964633feef5a290be634c2e718353b98def350be

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

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

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

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

以上錯誤的修復方式

針對上面的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()方法對參數(shù)進行ABI編碼,可以防止出現(xiàn)上述錯誤。

approveAndCall的正確打開方式

接著上面的代碼繼續(xù)說,除了上面的abi.encode對參數(shù)進行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會自動忽略前四個字節(jié),對后面的內(nèi)容進行ABI編碼。

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

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

之后approveAndCall方法內(nèi)的實現(xiàn)變?yōu)椋?/p>

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以及怎樣更好地使用它。區(qū)塊鏈是一個更新迭代迅速同時又極其強調(diào)安全的領域,對于權(quán)威組織給出的代碼,我們也不能簡單地copy-and-paste,審計和測試是必須的。

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

也可能因為approveAndCall和業(yè)務的聯(lián)系過于緊密,ERC20作為一個框架性的協(xié)議,這些細節(jié)并不在考慮范圍之內(nèi)。

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

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

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

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/24189.html

相關文章

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

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

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

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

    android_c 評論0 收藏0
  • 以太坊智能合約批量轉(zhuǎn)幣

    摘要:用途我們?yōu)槭裁葱枰哭D(zhuǎn)幣這樣的智能合約呢大大節(jié)約轉(zhuǎn)幣的資金成本。但是使用這個批量轉(zhuǎn)幣的智能合約,一般來說,兩百次左右可以一次性操作完,那么也就是兩百次轉(zhuǎn)幣費只需要支付一次轉(zhuǎn)幣費即可。大大節(jié)約轉(zhuǎn)幣的人工成本。 一直想寫這篇教程來著,因為你會發(fā)現(xiàn)網(wǎng)絡上很少有關于批量轉(zhuǎn)幣的詳盡的教程,一些提供該工具的網(wǎng)站也并不會將其智能合約代碼開源出來。雖然最終我們會發(fā)現(xiàn)原來這個批量轉(zhuǎn)幣的智能合約原來就這...

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

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

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

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

    kumfo 評論0 收藏0

發(fā)表評論

0條評論

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