在之前我們已經看到過,使用interface、library的方式調用外部合約的代碼。
接下來,我們將為大家補充第三種形式:
在下面的代碼中,部署cat合約之后,例如地址為 0x345678..
在部署animal合約時,傳遞此cat合約地址。從而能夠存儲合約的引用。調用test方法即可調用到外部合約的方法。
1 | pragma solidity ^0.4.23; |
不管是interface、library還是上面看到的形式,要調用外部代碼,都是底層調用了call或者是delecall函數。
call函數的使用方法,首先需要外部合約的地址。如下例中的animalCall合約,在部署合約時,傳遞了外部合約cat的地址 0x345678..
,存儲在address c當中。
通過合約地址.call(函數標志符)的方式來調用合約。函數標志符是對于函數聲明哈希之后的前4個字節的數據。
如下例中,c.call(bytes4(keccak256(“eat()”)))將調用cat合約中的eat方法
1 | contract cat{ |
call函數的返回值為true或者false。只有當能夠找到此方法并執行成功后,會返回true,而如果不能夠找到此函數或執行失則會返回false。因此調用test1方法會返回true,調用test2方法會返回false,因為找不到函數。
call函數如果找不到函數,默認會調用回調函數。
回調函數是特殊的函數,其沒有函數名。
其形式為:
1 | function(){ |
對于如下的cat合約。書寫了回調函數。假設合約地址為c.那么在外部調用c.call(“abc”);會找不到此函數,默認會執行回調函數.因此在外部調用的c.call(“abc”) 會使得cat合約的狀態變量變為999。而且call函數會返回true。
1 | contract cat{ |
回調函數是非常有用的,例如我們可以在外部調用失敗的時候,執行某一些操作。
對于如下的cat合約。書寫了回調函數。假設合約地址為c.那么在外部調用c.call(“abc”);會找不到此函數,默認會執行回調函數.回調函數中,將msg.data的值賦值給了fail變量。通過getfail函數可查看call函數傳遞過來的完整數據。fail變量的值為32個字節0x6162630000000000000000000000000000000000000000000000000000000000
,前3個字節是參數字母a、b、c的ASCII碼。61、62、63.
1 | pragma solidity ^0.4.23; |
在下例中,cat合約與animalcall合約中都有狀態變量我們首先部署cat合約,得到地址0x3456..
, 接下來,將合約地址作為參數部署anumalCall合約。
調用test2方法,其調用了cat合約的eat方法,修改了cat合約中a的值為256. call函數調用外部合約,修改外部合約中的狀態變量。
1 | pragma solidity ^0.4.23; |
delegatecall函數的使用方法和call函數一樣,通過合約地址.delegatecall(函數標志符)的方式來調用合約。函數標志符是對于函數聲明哈希之后的前4個字節的數據。
library庫的遠程調用正是使用了delegatecall函數。delegatecall與call不同之處在于,delegatecall不會修改外部合約中的狀態變量,其好像是將外部函數的代碼加載到了本地合約中執行。會修改本地合約狀態變量的值。
例如下面的代碼,首先部署cat合約,得到地址0x3456..
, 接下來,將合約地址作為參數部署anumalCall合約。
調用test2方法,其調用了cat合約的eat方法,但是卻是修改了animalcall合約中的狀態變量a。因此當查詢后發現,cat合約中的a并沒有變化,animalCall合約變量a變為了了256。
1 | pragma solidity ^0.4.23; |
call函數可以進行轉賬,并且是transfer與send的底層函數。call函數轉賬的使用方法是地址.call.value(轉賬金額)()
要注意的是,執行轉賬的時候,如果轉賬的地址為合約,并且轉賬合約中有回調函數。那么將默認會執行回調函數。
但是以太坊為了避免重入***,對于transfer與send函數進行了限制。當使用transfer與send函數,回調函數中執行的操作最多不能夠超過2300gas。這也就意味著不能夠執行轉賬、賦值等操作,而只能夠執行事件觸發等操作。
例如下面的代碼: 首先部署Receiver合約,得到地址0x3456..
,再傳遞Receiver的地址部署Sender合約。當調用sendMoney方法的時候,為合約地址0x3456..
轉賬的操作會觸發回調函數,將狀態變量balance的數量增加。但是由于修改狀態變量的操作超過了最大2300gas的限制,所以下面的操作不會成功。
1 | pragma solidity ^0.4.23; |
call函數能夠讓上面的操作成功。call函數能夠指定gas的限制,超過2300gas限制的約束。
如下例所示:
首先部署Receiver合約,得到地址0x3456..
,再傳遞Receiver的地址部署Sender合約。當調用sendMoney方法轉移100wei的時候,為合約地址0x3456..
轉賬的操作會觸發回調函數,將狀態變量balance的數量增加。由于call函數指定的最大gas限制為20317,所以觸發回調函數可以將balance的金額修改為100.但是要注意,正因為此,call函數是危險的底層函數,不能夠避免重入***的問題。
1 | pragma solidity ^0.4.23; |
本文鏈接: https://dreamerjonson.com/2018/11/23/solidity-48-call/
版權聲明: 本博客所有文章除特別聲明外,均采用 CC BY 4.0 CN協議 許可協議。轉載請注明出處!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。