摘要:假設(shè)某一天,星巴克突然宣布為了擁抱區(qū)塊鏈技術(shù),不再接受法幣買咖啡了,大家以后可以用以太幣或者星巴克自己發(fā)行的星星幣來(lái)買咖啡。用星星幣買咖啡星巴克自己發(fā)行了,取名,遵循協(xié)議。
什么是ERC20
ERC20是以太坊上為token提供的一種協(xié)議,也可以理解成一種token的共同標(biāo)準(zhǔn)。遵循ERC20協(xié)議的token都可以兼容以太坊錢包,讓用戶在錢包中可以查看token余額以及操作token轉(zhuǎn)賬,而不需要自己再手動(dòng)與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ò)上面的幾種方法,規(guī)定了一種token的基本信息、轉(zhuǎn)賬以及授權(quán)操作。這些操作基本可以覆蓋貨幣使用的絕大部分場(chǎng)景,該協(xié)議一經(jīng)提出后,立得到了開(kāi)發(fā)者的接納。
ERC20的局限ERC20雖然廣受開(kāi)發(fā)者喜愛(ài),但是依然有自己局限的一面。
讓我們先從一個(gè)大家十分熟悉的場(chǎng)景開(kāi)始談起。假設(shè)某一天,星巴克突然宣布為了擁抱區(qū)塊鏈技術(shù),不再接受法幣買咖啡了,大家以后可以用以太幣或者星巴克自己發(fā)行的星星幣來(lái)買咖啡。
首先,我們來(lái)看用以太幣來(lái)買咖啡的流程。
1. 用以太幣買咖啡簡(jiǎn)單寫(xiě)一個(gè)買咖啡的合約(注:偽代碼,僅表示邏輯)
contract BuyCoffee { function buy() public payable { starbucks.transfer(msg.value); COFFEE.transfer(msg.sender); } }
(熟悉ERC721的小伙伴肯定看出來(lái)了,這里的COFFEE是遵守ERC721的NFT token,本文重點(diǎn)講解的是ERC20,因此就不在贅述ERC721的實(shí)現(xiàn)了)。
整個(gè)調(diào)用過(guò)程如下圖:
客戶直接調(diào)用buy()方法,輸入買咖啡需要的以太幣數(shù)量,BuyCoffee合約就把自己有的COFFEE轉(zhuǎn)給客戶。整個(gè)過(guò)程只需要一步。
2. 用星星幣買咖啡星巴克自己發(fā)行了token,取名StarCoin,遵循ERC20協(xié)議。
那么BuyCoffee合約就要做一些小修改:(注:偽代碼,僅表示邏輯)
contract BuyCoffee { // 一杯咖啡的StarCoin價(jià)格 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); } }
整個(gè)買咖啡的過(guò)程如下圖:
圖中可以看到,因?yàn)?b>StarCoin和BuyCoffee是兩個(gè)合約,分別有自己獨(dú)立的地址,所以客戶買咖啡就要經(jīng)過(guò)兩次操作:
先要把買咖啡的starcoin數(shù)量授權(quán)給BuyCoffee;
然后調(diào)用BuyCoffee中的buy(uint)方法買咖啡;
3. 以太幣 vs 星星幣通過(guò)上面的分析可以看到,如果要使用星巴克發(fā)行的StarCoin進(jìn)行付款的話,買一杯咖啡要操作兩次,無(wú)疑這增加了操作成本,并且很反常識(shí)。一個(gè)很好的辦法就是把StarCoin和BuyCoffee合二為一,如果token邏輯和業(yè)務(wù)邏輯都在同一個(gè)合約里的話,就不存在上述問(wèn)題了。
這看上去是一個(gè)不錯(cuò)的辦法,然而治標(biāo)不治本。萬(wàn)一以后星巴克還宣布可以使用星星幣買積分、參加優(yōu)惠活動(dòng)甚至直接參與星巴克公司分紅,鑒于智能合約不可更改的特點(diǎn),這么多業(yè)務(wù)邏輯不可能一開(kāi)始就全部規(guī)劃好,以后的新業(yè)務(wù)依然面臨多次操作的問(wèn)題。
approveAndCallapproveAndCall方法可以完美地解決上述問(wèn)題,把兩次操作合并為一次,讓用戶在付款時(shí)感覺(jué)不到這些復(fù)雜的操作。
使用approveAndCall方法之后,整個(gè)操作的流程如下:
用戶在token合約 (StarCoin) 中授權(quán)一筆token給業(yè)務(wù)合約 (BuyCoffee), 通過(guò)token合約中的approveAndCall方法;
token合約通知業(yè)務(wù)合約,它已經(jīng)被授權(quán)可以操作用戶的一筆token,通過(guò)調(diào)用業(yè)務(wù)合約的receiveApproval方法;
業(yè)務(wù)合約就可以把用戶的token轉(zhuǎn)給自己,然后自己再去完成相關(guān)的業(yè)務(wù)邏輯(比如把咖啡轉(zhuǎn)給用戶,或者自己再做一些轉(zhuǎn)賬操作)。
整個(gè)過(guò)程就如下圖:
這就需要在token合約里創(chuàng)建approveAndCall方法,如下:
function approveAndCall(address _to, uint256 _value, bytes _extraData) { approve(_to, _value); ApproveAndCallFallBack(_to).receiveApproval( msg.sender, _value, extraData) }
(參數(shù)的個(gè)數(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使用注意事項(xiàng)
為什么要使用approveAndCall以及怎樣使用它,上文已經(jīng)解釋清楚了。有些可能覺(jué)得再多寫(xiě)一個(gè)ApproveAndCallFallBack接口有些多此一舉,不如直接使用address(_to).call(...)來(lái)的簡(jiǎn)單直接。
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; } }
想看全部源碼的可以訪問(wèn):https://github.com/ConsenSys/...
但是大家如果稍加嘗試就會(huì)發(fā)現(xiàn),如果這里的_extraData超過(guò)32個(gè)字節(jié),就會(huì)報(bào)錯(cuò)。
舉個(gè)例子:
下面是長(zhǎng)度為64字節(jié)的bytes (換行只是為了讓大家看著不費(fèi)力) :
0x0000000000000000000000000000000100000000000000000000000000000001 000000000000000000000000964633feef5a290be634c2e718353b98def350be
它的ABI編碼如下 (換行只是為了讓大家看著不費(fèi)力) :
0x0000000000000000000000000000000100000000000000000000000000000060 0000000000000000000000000000000100000000000000000000000000000040 0000000000000000000000000000000100000000000000000000000000000001 000000000000000000000000964633feef5a290be634c2e718353b98def350be
第一行(第一個(gè)32byte):距離參數(shù)開(kāi)始位置的偏移量;
第二行(第二個(gè)32byte):bytes參數(shù)的長(zhǎng)度;
第三行和第四行(最后64個(gè)byte):bytes參數(shù)的內(nèi)容;
所以上面的bytes參數(shù)如果超過(guò)32byte長(zhǎng)度,第二個(gè)32byte就會(huì)被當(dāng)成bytes參數(shù)的長(zhǎng)度,最后因?yàn)?b>out of gas而導(dǎo)致調(diào)用失敗。
以上錯(cuò)誤的修復(fù)方式針對(duì)上面的ConsenSys公司的代碼,正確寫(xiě)法應(yīng)該是:
/* 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()方法對(duì)參數(shù)進(jìn)行ABI編碼,可以防止出現(xiàn)上述錯(cuò)誤。
approveAndCall的正確打開(kāi)方式接著上面的代碼繼續(xù)說(shuō),除了上面的abi.encode對(duì)參數(shù)進(jìn)行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會(huì)自動(dòng)忽略前四個(gè)字節(jié),對(duì)后面的內(nèi)容進(jìn)行ABI編碼。
還有一個(gè)使代碼看上去更加簡(jiǎn)潔的代碼方式就是上面提到的,增加ApproveAndCallFallBack接口:
interface ApproveAndCallFallBack { function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public; }
之后approveAndCall方法內(nèi)的實(shí)現(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; }
以上代碼貢獻(xiàn)自:寫(xiě)在最后https://github.com/evolutionl...
注:這是一個(gè)以太坊上的沙盤游戲。其中RING token的設(shè)計(jì)目的之一就是為了在游戲中買賣地塊,感興趣的同學(xué)可以詳細(xì)研究其中的erc20和erc721token之間的交互方式。
這一篇解釋了為什么使用approveAndCall以及怎樣更好地使用它。區(qū)塊鏈?zhǔn)且粋€(gè)更新迭代迅速同時(shí)又極其強(qiáng)調(diào)安全的領(lǐng)域,對(duì)于權(quán)威組織給出的代碼,我們也不能簡(jiǎn)單地copy-and-paste,審計(jì)和測(cè)試是必須的。
至于ERC20為什么沒(méi)有把approveAndCall添加進(jìn)協(xié)議中,可能早期在以太坊上流通的大部分多為token合約,還沒(méi)有能夠建立去較為復(fù)雜的應(yīng)用強(qiáng)的程序,因此更加強(qiáng)調(diào)的是token作為貨幣具有的流通手段的職能;隨著以太坊生態(tài)的發(fā)展出現(xiàn)了越來(lái)越多的應(yīng)用,這時(shí)ERC20 token的支付手段的職能才被大家重視起來(lái)。
也可能因?yàn)?b>approveAndCall和業(yè)務(wù)的聯(lián)系過(guò)于緊密,ERC20作為一個(gè)框架性的協(xié)議,這些細(xì)節(jié)并不在考慮范圍之內(nèi)。
鑒于智能合約的不可更改性,希望今后的發(fā)行token的組織機(jī)構(gòu)或者個(gè)人,在實(shí)現(xiàn)ERC20的基礎(chǔ)上,可以盡可能安全地實(shí)現(xiàn)approveAndCall方法,使得基于token的應(yīng)用生態(tài)更加魯棒。
最后提醒,ERC223的tokenFallback方法也有類似的效果,如果大家感興趣也可以自己做進(jìn)一步的研究。友情提醒:ERC223的tokenFallback方法在之前提到的https://github.com/evolutionl...,感興趣的朋友可以自行參考。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/24189.html
摘要:本文就來(lái)剖析下什么是是什么在創(chuàng)建代幣一篇,我們講到過(guò)代幣,和一樣,同樣是一個(gè)代幣標(biāo)準(zhǔn),官方簡(jiǎn)要解釋是,簡(jiǎn)寫(xiě)為,多翻譯為非同質(zhì)代幣。返回合約代幣符號(hào),盡管是可選,但強(qiáng)烈建議實(shí)現(xiàn),即便是返回空字符串。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:剖析非同質(zhì)化代幣ERC721-全面解析ERC721標(biāo)準(zhǔn)原文已更新,請(qǐng)讀者前往原文閱讀 什么是ERC-721?現(xiàn)在我們看到的各種加密貓貓狗狗都是基于ERC...
摘要:從這節(jié)開(kāi)始,我們將學(xué)習(xí)代幣標(biāo)準(zhǔn)以及加密收集資產(chǎn)等知識(shí)。聲明一個(gè)繼承的新合約,命名為。注意目前是一個(gè)草稿,還沒(méi)有正式商定的實(shí)現(xiàn)。所以把這一個(gè)可能的實(shí)現(xiàn)當(dāng)作考慮,但不要把它作為代幣的官方標(biāo)準(zhǔn)。 從這節(jié)開(kāi)始,我們將學(xué)習(xí)代幣, ERC721標(biāo)準(zhǔn), 以及加密收集資產(chǎn)等知識(shí)。 一、代幣 代幣 讓我們來(lái)聊聊以太坊上的代幣。 如果你對(duì)以太坊的世界有一些了解,你很可能聽(tīng)過(guò)人們聊到代幣——尤其是 ERC2...
摘要:用途我們?yōu)槭裁葱枰哭D(zhuǎn)幣這樣的智能合約呢大大節(jié)約轉(zhuǎn)幣的資金成本。但是使用這個(gè)批量轉(zhuǎn)幣的智能合約,一般來(lái)說(shuō),兩百次左右可以一次性操作完,那么也就是兩百次轉(zhuǎn)幣費(fèi)只需要支付一次轉(zhuǎn)幣費(fèi)即可。大大節(jié)約轉(zhuǎn)幣的人工成本。 一直想寫(xiě)這篇教程來(lái)著,因?yàn)槟銜?huì)發(fā)現(xiàn)網(wǎng)絡(luò)上很少有關(guān)于批量轉(zhuǎn)幣的詳盡的教程,一些提供該工具的網(wǎng)站也并不會(huì)將其智能合約代碼開(kāi)源出來(lái)。雖然最終我們會(huì)發(fā)現(xiàn)原來(lái)這個(gè)批量轉(zhuǎn)幣的智能合約原來(lái)就這...
摘要:目前市面上,凡是基于以太坊的令牌,在交易所上線交易的均是令牌,那么今天我們就來(lái)聊聊令牌的標(biāo)準(zhǔn)方案吧。 0x00 寫(xiě)在前面 眾所周知,以太坊在現(xiàn)階段最大的應(yīng)用就是令牌發(fā)行,而在以太坊中有很多類型的令牌,最著名的當(dāng)屬ERC20了,但是對(duì)于其他幾種令牌類型,可能還有一些朋友不知道,所以最近規(guī)劃了一個(gè)系列,就是以太坊標(biāo)準(zhǔn)令牌系列。 目前市面上,凡是基于以太坊的令牌,在交易所上線交易的均是ERC...
摘要:前提是擁有者必須要通過(guò)某些機(jī)制對(duì)這個(gè)請(qǐng)求進(jìn)行確認(rèn),比如通過(guò)進(jìn)行。事件,當(dāng)被調(diào)用時(shí),需要觸發(fā)該事件。允許從中轉(zhuǎn)出的數(shù)增加所有者允許花費(fèi)代幣的數(shù)量。已經(jīng)歸屬合約,其余歸還給所有者。計(jì)算已歸屬但尚未釋放的金額。源碼分析到這里就結(jié)束了。 ERC20:Ethereum Request for Comments 20,是一個(gè)基于以太坊代幣的接口標(biāo)準(zhǔn)(協(xié)議)。所有符合ERC-20標(biāo)準(zhǔn)的代幣都能立即兼...
閱讀 1776·2021-10-27 14:15
閱讀 3835·2021-10-08 10:12
閱讀 1168·2021-09-22 15:55
閱讀 3230·2021-09-22 15:17
閱讀 834·2021-09-02 15:40
閱讀 1748·2019-08-29 18:33
閱讀 1099·2019-08-29 15:22
閱讀 2355·2019-08-29 11:08