It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

【Ethereum】コントラクトから外部のコントラクトの関数を実行する

EthereumのContractにおいて、 Contract内部から他のContractの関数を呼び出したいケースがあります。 その時に考えられる方法としては主に以下の2つかと思います。

  • ①Contract内で別のContractをnewする
  • ②既にデプロイされている外部のContractを使う

今回は、②の方法について検証してみました。

①の場合は特に意識をする必要ないですが、②の場合は既にデプロイされているContractを使うため、外部から値を受け取る必要があり、今回はContractのaddressを受取るという方法をとっています。

デプロイ済みの(共通利用される)Contract

pragma solidity ^0.4.11;

contract MyToken {
    uint256 public totalSupply;

    mapping (address => uint256) public balanceOf;

    function MyToken(address owner) {
        totalSupply = 10000;
        balanceOf[owner] = 1000;
    }

    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        if (_to == 0x0) revert();
        if (balanceOf[_from] < _value) revert();
        if (balanceOf[_to] + _value < balanceOf[_to]) revert();
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        return true;
    }
}
  • 簡単なTokenを作成しています
  • このTokenは、送信用の関数としてtransferFromを持っています
  • このTokenは既にデプロイされている想定でこのContractのアドレスを使用します
  • 簡単のため、コンストラクタで渡されるアカウントのアドレスに1000Tokenを渡しています

MyTokenを利用するContract

pragma solidity ^0.4.11;

// 呼び出す対象のコンストラクタはimportする必要がある
import './MyToken.sol';

contract TokenCaller {
    MyToken public token;
  
    function TokenCaller(address _token) {
        require(_token != 0x0);
        // コンストラクタでアドレスからMyTokenObjectに変換
        token = MyToken(_token);
    }

    function transfer(address from, address to, uint256 total) returns (bool) {
        // tokenからtransferFromを実行する
        return token.transferFrom(from, to, total);
    }
}
  • Tokenを利用する側のContractです
  • コンストラクタでTokenのアドレスを受取りTokenのオブジェクトを生成します
  • 関数transferは、生成したTokenのtransferを内部で呼び出しTokenの送信処理を行います

動作確認

動作確認は、truffleを使ってtestRPC上で実行しています。

var TokenCaller = artifacts.require('../contracts/TokenCaller.sol');
var MyToken = artifacts.require('../contracts/MyToken.sol');

contract('TokenCaller', function(accounts) {
    it ('test', async function() {
        // MyTokenのデプロイ
        let token = await MyToken.new(accounts[1])
        // Callerのデプロイ
        let caller = await TokenCaller.new(token.address)
        // Tokenの送り先のaccount[2]にtokenがないことを確認
        console.log(await token.balanceOf.call(accounts[2]));
        // account1からaccount2にトークンを100送る
        await caller.transfer(accounts[1], accounts[2], 100)
        // account2のトークンを確認
        console.log(await token.balanceOf.call(accounts[2]));
    });
});

結果

  Contract: TokenCaller
{ [String: '0'] s: 1, e: 0, c: [ 0 ] }
{ [String: '100'] s: 1, e: 2, c: [ 100 ] }
  • account2にトークンが送られていることが確認できました。

参考

https://dappsforbeginners.wordpress.com/tutorials/interactions-between-contracts/