Invoke Contract Method From Another Contract

Ethereum的設計上允許智能合約與智能合約之間可以彼此呼叫,這讓合約間的溝通成為了可能,這部分也衍伸出了很多不一樣的應用,類似去中心化交易所,多重簽名錢包(Multisig Wallet),以下將會介紹智能合約彼此的呼叫方法以及如何撰寫之間的Test Case。

Contract

contract Caller {
event Log(address _address, uint256 _value, bytes _data);
  function invoke(address _address, uint256 _value, bytes _data) {
if (_address.call.value(_value)(_data)) {
Log(_address, _value, _data);
}
}
}
contract Callee {
uint256 public counter;

function addOne() {
counter = counter + 1;
}
  function addNum(uint256 num) {
counter = counter + num;
}
}

上述兩個智能合約分別為Caller與Callee,Caller是呼叫者,可以透過invoke來調用別的合約的函數,Callee提供兩個函數一個是addOne,單純將counter加一,另外一個是addNum可以允許將counter加上不等的數值。

_address.call.value(_value)(_data)

這段程式碼可以透過呼叫指定的位址(_address)將ETH(_value)與執行的bytecode(_data)從合約內部呼叫外部合約函數來執行。

Truffle test

透過truffle要測試跨合約調用,則需要注意一點,在於data的部分要怎麼將想要的資訊轉換成EVM看得懂的bytecode。這邊就需要透過ethereumjs-abi這個library提供轉換。

var Caller = artifacts.require("Caller.sol");
var Callee = artifacts.require("Callee.sol");
var abi = require("ethereumjs-abi");
contract("Caller and Callee", function(accounts) {
it("invoke callee via caller", function() {
var caller;
var callee;
return Caller.deployed().then(function(callerInstance) {
caller = callerInstance;
return Callee.deployed().then(function(calleeInstance) {
callee = calleeInstance;
// 透過abi將method encode之後再餵給invoke方法,addOne是function name,而 [] 則是參數型別
var encoded = abi.methodID("addOne", []).toString("hex");
return caller.invoke(callee.address, 0, "0x" + encoded);
}).then(function() {
return callee.counter.call();
}).then(function(counter) {
assert.equal(counter.toNumber(), 1, "count wasn't correctly");
}).then(function() {
// 透過abi將raw data encode後塞到函數後面,整串打包後再餵給invoke
var encoded = abi.methodID("addNum", [ "uint256" ]).toString("hex") + abi.rawEncode([ "uint256" ], [ 10 ]).toString("hex");
return caller.invoke(callee.address, 0, "0x" + encoded);
}).then(function() {
return callee.counter.call();
}).then(function(counter) {
assert.equal(counter.toNumber(), 11, "count wasn't correctly");
});
});
});
});

透過abi.methodID將function name轉換成bytecode與透過abi.rawEncode將參數轉成bytecode之後餵給要執行的函數。

註:這邊有一個重點需要注意,因為bytes這個型別會無法判斷外部帶入的參數是否是string,所以要在前面多加一個0x,讓EVM可以判斷是要使用bytes呼叫。

Like what you read? Give Phyrex Tsai a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.