Ethereum 外部コントラクトの呼び出し方法(Remix, MetaMask連携)


【本文】

Ethereumのスマートコントラクトを開発する際、別のコントラクトを呼び出し実行するケースが出てくると思います。
今回は、シンプルなコードで呼び出し事例を紹介します。内容は初心者向けです。

RemixでSolidityベースのスマートコントラクトを開発し、MetaMaskを使い、Ropsten(テストネットワーク)にデプロイしています。本格的にやる場合は別ですが、少し触ってみたいとか、社内向け勉強会で使用する場合等は、特に環境構築で頑張らなくても、Google Chrome上でスマートコントラクトの開発・デプロイ・実行が簡単にできるので、このやり方は覚えておくと良いです。
なお、MetaMaskの導入方法やRemixとRopsten Test Netとの連携方法等は【参考資料】で掲載しているリンクもご参考ください。

【スマートコントラクト】

今回のサンプル

testCalc

  • calcNumメソッド: input値(uint)を与え、加算。
  • getNumメソッド: Storageからxに格納された値(計算結果)を参照。
testCalc.sol
pragma solidity ^0.4.24;
contract testCalc {
    uint x;
    //計算
    function calcNum(uint _x) external{
        x = x + _x;
    }
    //計算結果取得
    function getNum() public view returns(uint){
        return x;
    }
}

testCalcCaller

  • 参照先のContract(今回は「testCalc」)をimport。
  • コンストラクタで、参照先のContractアドレスを取得しオブジェクトにする。
  • CntParcalcValueメソッド: 参照先contractの計算メソッド(calcNum)を呼び出す。
testCalcCall.sol
pragma solidity ^0.4.24;
// 参照先のコントラクトをimport
import './testCalc.sol';
contract testCalcCaller {
    testCalc public cntAddr;
    //コンストラクタ
    constructor(address _cntAddr) public {
        require(_cntAddr != 0x0);
        //オブジェクト
        cntAddr = testCalc(_cntAddr);
    }
    function CntParcalcValue(uint a) external {
        // cntAddrからcalcNumを実行する
        cntAddr.calcNum(a);
    }
}

【デプロイ】

  • (図1)Remix (https://remix.ethereum.org/) をGoogle Chrome上で開き、上記のコントラクトをそれぞれ別ファイルとして作成する。(ファイル名:「xxx.sol」)
  • この時、Remixの右上タブで「Compile」を選択し、エラーになっていないことを確認しておく。(図は割愛。)

図1

  • (図2-3)MetaMaskでテストネットワーク(今回はRopsten)を選択しておく。MetaMaskの左上にあるキツネの絵の右隣のプルダウンメニューでMain Ethereum Networkも含め自由に選択可能。(Mainは本番用のネットワーク。)

図2

図3

  • Remix上で「Run」より「Environment: injected Web3」(これでRopstenとRemixが連携される。)を選択し、「コントラクト名: testCalc」を選び、「Deploy」ボタンを押下。
  • (図4)MetaMaskが勝手に立ち上がるので、Gas Price(1~4辺り)を設定し、「SUBMIT」ボタンを押下。

図4

図5

  • (図6)Remixのコンソール上にデプロイ内容が表示され、右枠にデプロイしたContractアドレス(0x66cc348dee962a7203f5ad72f4e761b059bc09fd)とメソッド等の実行ボタンが表示される。(図例では、「calcNum」と「getNum」)このボタンを通じて、Contractを実行することが可能となる。

図6

  • (図7)「getNum」実行結果、「uint256: 0」となっており、まだ計算されていないことを確認。

図7

  • (図8)Remix上で今度は「testCalcCall」コントラクトをデプロイする。やり方は、「testCalc」と同じだが、「testCalc」を参照する必要があるので、デプロイ時に「testCalc」のContractアドレス(0x66cc348dee962a7203f5ad72f4e761b059bc09fd)を指定する必要がある。指定したら、「transact」ボタンを押下し実行。
  • MetaMaskが自動的に立ち上がるが、やり方は「testCalc」と同じなので図は割愛。

図8

  • (図9)Remixのコンソール上にデプロイ内容が表示され、「testCalc」と同様に、右枠にデプロイしたContractアドレス(0x323d96b422e8420fba56cc95d26229e7bc645293)とメソッド等の実行ボタンが表示される。(図例では、「CntParcalcValue」と「cntAddr」)このボタンを通じて、Contractを実行することが可能となる。
  • 参考として今回デプロイしたTxへのリンクを貼っておく。:「 https://ropsten.etherscan.io/tx/0x2418485429ce880a37cb6cd9f91ebaea7f7fe8e999d000a37838e28442390962

図9

【コントラクト実行】

  • 今回は、「testCalcCall」から「testCalc」コントラクトの「calcNum」メソッドを実行するので、Remix上で「testCalcCall」の「CntParcalcValue」メソッドに適当な正数を入力し、コントラクトを実行する。実行の度にMetaMaskが立ち上がるが、図は割愛する。(要は適当なGas Price」を入力し、「SUBMIT」ボタンを押下すればよい。)
  • (図10)始めに、「CntParcalcValue」に引数「10」を入力し、コントラクト実行。
  • 参考Tx:「 https://ropsten.etherscan.io/tx/0x572472f975b428ec5edfd117777d419438515eb56a637903d6c423685fdfdc62
  • Remix上で、「testCalc」の「getNum」メソッドを実行すると、「uint256: 10」となり、「testCalcCall」コントラクトから「testCall」コントラクトの「calcNum」メソッドが実行されたことがわかる。

図10

  • (図11)続いて、「CntParcalcValue」に引数「5」を入力し、コントラクト実行。
  • 参考Tx:「 https://ropsten.etherscan.io/tx/0x61af48306271f9fbca3a77a7f9d4c2ee5d1ac210c3773d9b598438a71f23be78
  • Remix上で、「testCalc」の「getNum」メソッドを実行すると、「uint256: 15」となり、「testCalcCall」コントラクトから「testCall」コントラクトの「calcNum」メソッドを通じて加算処理が実行されたことがわかる。

図11

  • ポイントの一つとして、今回のコントラクトで「getNum」は「view」修飾子を使用している。この「view」を使用することで、トランザクションを発行せずに(つまりgasをかけずに)、直接ストレージに格納された値を参照することが出来る。gasの使用はコントラクト実行者にとっても、またEthereumネットワークにとってもコストや負荷を軽減することになるので、ストレージへの書き込みが不要で読込だけで済む場合は、「view」を使用することが推奨される。

【参考資料】

MetaMask,Remix関連
・「Learning Solidity Part 1: Contract Dev with MetaMask」(Karl Floersch さん)
・「RemixとMetaMaskを使って、Smart Contractの開発環境を整えてみた」(Qiita投稿 by @shiki_tak さん)

Ethereumのブロックチェーン構造
・「Basics of the Ethereum Blockchain」(GitHub: [Japanese] Ethereum Development Tutorial)

requireとgasとの関係について
・「Solidityのassertとrequireとrevertの違い(アルゴリズムとかオーダーとか)」(なかじょ さん)