【本文】
Solidityを使用したスマートコントラクトのinterface実装についてのサンプルと簡単な解説となります。
トークンモデルを標準化したERC20を実装したコントラクトを見ていると、しばしばERC20をinterfaceとして実装しているケースがあり、interfaceについてある程度理解しておくことも重要です。
interfaceは他の言語と基本的な考え方は変わりませんが、誤解を招く言い方にはなりますが、Solidityでは例えばERC20と言った標準化されたルールを規定したものと捉えておくと良いです。
(もし、ルールが定まらず各人が勝手な仕様でトークンを発行すると、etherとトークン、あるいはトークン同士を交換する際に大変になる、と言った事態を避ける。)
interfaceやeventと言った詳細な説明(と言ってもinterfaceは制約事項くらいしか書かれていないが。)はsolidityの公式ドキュメントを参照することをお勧めします。
【サンプル】
[test0624]
- test0624はtest0624Interface0624を実装する。
- 実装時の約束事としてinterface0624で定義された関数の型を使用しなければならない。
- interface0624で定義されたイベントを出力する為、emitを使用している。これによって、test0624で実装したcalcNumメソッドの実行ログが出力される。
- デプロイする際は、contractのみで、interface自体はデプロイできない。
test0624.sol
pragma solidity ^0.4.24;
import './test0624Interface.sol';
//test0624Interface.solを実装
contract test0624 is test0624Interface {
// test0624Interface.solで定義された関数(calcNum)を実装
function calcNum(uint256 _num) public returns (bool success){
num = num + _num;
// test0624Interface.solで定義されたイベントを出力
emit CalcNum(msg.sender, _num);
return true;
}
}
[test0624Interface]
- test0624Interfaceはinterfaceであり、関数の型やイベントの定義と言った最低限の実装のみ行う。なお、solidityでは、interfaceは他のinterfaceを実装することはできない。
- interfaceを実装するcontractは、interfaceで定義された関数の型を全てその通りに実装しなければならない。
- interfaceで定める関数は型のみなので、それを実装したcontract側で具体的な処理を記述すればよい。
test0624Interface.sol
pragma solidity ^0.4.24;
//インターフェース
contract test0624Interface {
uint256 public num;
//関数の型を定義
function calcNum (uint256 _num) public returns(bool success);
//イベント定義
event CalcNum(address indexed _owner, uint256 _num);
}
簡単な説明(interfaceとcontractの関係)
- contractがinterfaceを実装する際、以下の通り、interfaceのファイルをimportし、contract名に「contract A is B(interface) { ... }」のように記述する。
import './test0624Interface.sol';
//test0624Interface.solを実装
contract test0624 is test0624Interface {
...
}
- Interfaceで以下の通り関数を定義しているが、単に関数の型(関数名、引数、戻り値)だけ実装している。実際の処理内容は実装するcontract側で記述することになる。
//関数の型を定義
function calcNum (uint256 _num) public returns(bool success);
- イベント定義は以下の通り。
//イベント定義
event CalcNum(address indexed _owner, uint256 _num);
- interfaceを実装したcontract側では、関数をinterfaceで定義された通りのまま記述し、具体的な処理内容をメソッド内で定義している。
- 具体的な処理は単に引数を基に加算処理するだけのもの。(処理内容自体は何でもよい。)
- 「emit」の箇所が、interfaceで実装したイベント定義となる。
- 最後に、戻り値を成功した場合のbool型(真偽)を返している。
function calcNum(uint256 _num) public returns (bool success){
num = num + _num;
// test0624Interface.solで定義されたイベントを出力
emit CalcNum(msg.sender, _num);
return true;
}
【実行結果】
- 今回は、Remixを使ってローカルのメモリ上で疑似デプロイする。上述の通り、デプロイするのは、contractのみ。サンプルでは、「test0624」をデプロイすることになる。
- Remix上の「Run」タブで「Environment: JavaScript VM」を選択し、「Deploy」ボタン押下でメモリ上に疑似デプロイされる。
- 疑似デプロイ後、「calcNum」メソッドをボタン押下で実行。
- 実行結果は(図1)の通り。
- (図2)Remix上のコンソール画面にlogsと言う項目が出力されているが、これが、Interfaceで定義したイベントを、contract側でemitによって出力した結果となる。つまり、Eventの実態はlogと言うことになる。
- 画像では少し見え辛いが、logsの中に「topic」があり、ハッシュ値(0x4655cc4631771176f71ef2de57c20fdb8da6ca3eb57d6a7b09f8751f2fd7144d)が割り当てられていることが分かる。実は、このtopicsはinterfaceで定義した「CalcNum(address indexed _owner, uint256 _num)」をハッシュ化したものとなる。
【まとめ】
interfaceを作成することで、contractを標準化することが可能になります。実際、ERC20と言ったトークン標準化に準拠したトークンモデルの多くは、しばしばinterfaceを用いて実装されていることが分かります。その意味では、interfaceは共通ルールを規定するもの、とも言えそうです。
また、interfaceを用いることで、contract側の処理内容を変更したい場合はinterfaceを実装したcontractのメソッド内で修正すればよいとか、contractの可読性を上げるのにも使用できます。
【参考文献】
Solidity: Contractについて
ERC20(EIP20)、事例含む