3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

EthereumAdvent Calendar 2018

Day 3

Solidity で overload したメソッドを呼び出す

Last updated at Posted at 2018-12-02

はじめに

これは Ethereum Advent Calendar 2018 の3日目です。
2日目は Morinikiz さんです。

自己紹介

この記事について

Solidity ではメソッドの overload が可能なのですが、truffle で素直にテストを書いたらエラーになった、という事がありました。
今回はその際の回避方法について説明していきます。

準備

環境

$ truffle version
Truffle v4.1.14 (core: 4.1.14)
Solidity v0.4.24 (solc-js)

Solidity

overload しているメソッドを含む、このようなコントラクトを準備します。

pragma solidity ^0.4.22;

contract OverloadContract {

  mapping(address => uint256) internal voteMap;

  event Vote(
    address indexed _to,
    uint256 _count
  );

  event Clear(
    address indexed _to,
    uint256 _clearCount
  );

  function vote(address _to) external {
    voteMap[_to]++;
    emit Vote(_to, 1);
  }

  function vote(address _to, uint256 _count) external {
    voteMap[_to] += _count;
    emit Vote(_to, _count);
  }

  function clear() external {
    uint256 count = voteMap[msg.sender];
    voteMap[msg.sender] = 0;
    emit Clear(msg.sender, count);
  }
}

テストコード

全てのメソッドを走らせるだけのシンプルなテストを用意します。

const OverloadContract = artifacts.require("OverloadContract")

contract('OverloadContract', (accounts) => {
  let obj
  const voteTo = accounts[1]

  before(async () => {
    obj = await OverloadContract.new()
  })

  it('vote', async () => {
    await obj.vote(voteTo)
  })

  it('vote with count', async () => {
    await obj.vote(voteTo, 10)
  })

  it('clear', async () => {
    await obj.clear({from: voteTo})
  })
})

テストする

$ truffle test test/OverloadContract.test.js
Using network 'development'.



  Contract: OverloadContract
    1) vote
    > No events were emitted
    ✓ vote with count (151ms)
    ✓ clear (55ms)


  2 passing (379ms)
  1 failing

  1) Contract: OverloadContract
       vote:
     Error: Invalid number of arguments to Solidity function
      at Object.InvalidNumberOfSolidityArgs (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/errors.js:25:1)
      at SolidityFunction.validateArgs (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/function.js:74:1)
      at SolidityFunction.toPayload (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/function.js:90:1)
      at SolidityFunction.sendTransaction (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/function.js:163:1)
      at SolidityFunction.execute (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/function.js:256:1)
      at /usr/local/lib/node_modules/truffle/build/webpack:/packages/truffle-contract/contract.js:204:1
      at new Promise (<anonymous>)
      at /usr/local/lib/node_modules/truffle/build/webpack:/packages/truffle-contract/contract.js:155:1
      at process._tickCallback (internal/process/next_tick.js:68:7)

Error: Invalid number of arguments to Solidity function すなわちメソッドの引数が異なるよ!というエラーが返ってきます。
見ると vote(voteTo) が失敗して vote(voteTo, 10) が成功している。
つまり contract.vote は引数2つの方が採用されていて、vote(voteTo) だと引数が足らずにエラーとなっているようです。
こうなると overload したメソッドのテストが書けないので、困ってしまいます。

エラーを回避

[] で引数を指定する

どちらの vote を使うか引数指定によって切り替える事ができるので、それを利用できます。

const OverloadContract = artifacts.require("OverloadContract")

contract('OverloadContract', (accounts) => {
  let obj
  const owner = accounts[0]
  const voteTo = accounts[1]

  before(async () => {
    obj = await OverloadContract.new({from: owner})
  })

  it('vote', async () => {
    await obj.contract.vote['address'](voteTo, {from: owner})
  })

  it('vote with count', async () => {
    await obj.contract.vote['address,uint256'](voteTo, 10, {from: owner})
  })

  it('clear', async () => {
    await obj.clear({from: voteTo})
  })
})

web3 1.0 系を使う

試してみたところ web3 1.0 系 であれば引数の数によって呼び出したい Solidity function を切り替えてくれるようで、こちらも利用できそうです。
truffle test で利用できる web3 は 0.20 系なので、1.0 系を使うように書く必要があります。

const OverloadContract = artifacts.require("OverloadContract")
const Web3 = require('web3')

contract('OverloadContract', (accounts) => {
  let contract
  const owner = accounts[0]
  const voteTo = accounts[1]

  before(async () => {
    const web3Newer = new Web3(web3.currentProvider)
    const obj = await OverloadContract.new({from: owner})
    contract = new web3Newer.eth.Contract(obj.abi, obj.address)
  })

  it('vote', async () => {
    await contract.methods.vote(voteTo).send({from: owner})
  })

  it('vote with count', async () => {
    await contract.methods.vote(voteTo, 10).send({from: owner})
  })

  it('clear', async () => {
    await contract.methods.clear().send({from: voteTo})
  })
})

去年こちらにあるような事例が報告されていたのですが、どこかで解消された模様?

おわりに

これで安心して、自作したメソッドを網羅するスマートコントラクトのテストコードが書けそうです。
truffle の進化によってこのあたり解消する可能性もあるので、その際は truffle 標準なやり方にシフトするのが良いかと思います。

明日は

Hirata Takahiro さんです。

3
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?