1

I'm a beginner trying to interact with a simple contract via web3, and I'm having some problems that I can't figure out.

I've written a basic contract with a struct and two functions. This is basically copied from here: Returning a struct and reading via Web3

contract MyContract {
    struct MyStruct {
        address a;
        uint b;
    }

    MyStruct[] public MyStructs;

    function getStruct(uint index) public constant returns (address, uint) {
        return (MyStructs[index].a, MyStructs[index].b);
    }

    function addStruct(address _a, uint _b) public returns (uint){
        MyStruct memory a = MyStruct(_a, _b);
        MyStructs.length++;
        MyStructs.push(a);
        return MyStructs.length;
    }
}

I have two test functions that I'm invoking using truffle's test feature. These are basically copied from http://truffleframework.com/docs/getting_started/javascript-tests

var MyContract = artifacts.require("MyContract");


contract('MyContract', function() {
  it("should create a stuct", function() {
    return MyContract.deployed().then(function(instance) {
      return instance.addStruct.call(0xf17f52151EbEF6C7334FAD080c5704D77216b732, 1000);
    }).then(function(length) {
       console.log(length);
       assert.equal(length, 1, "length is not 1");

    });
  });

  it("get the struct that was previously created", function() {
    return MyContract.deployed().then(function(instance) {
      return instance.getStruct.call(0);
    }).then(function(values) {
      console.log(values);
      assert.equal(values, (0xf17f52151EbEF6C7334FAD080c5704D77216b732, 1000));
    });
  });
});

When I run this, I get the following:

  Contract: MyContract { [String: '2'] s: 1, e: 0, c: [ 2 ] }
    1) should create a stuct
    > No events were emitted
    2) get the struct that was previously created
    > No events were emitted


  0 passing (190ms)   2 failing

  1) Contract: MyContract should create a stuct:
     AssertionError: length is not 1: expected { Object (s, e, ...) } to equal 1
      at test/mycontract.js:10:15
      at process._tickCallback (internal/process/next_tick.js:103:7)

  2) Contract: MyContract get the struct that was previously created:
     Error: Invalid JSON RPC response: {"id":21,"jsonrpc":"2.0"}
      at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:41483:16)
      at /usr/local/lib/node_modules/truffle/build/cli.bundled.js:330353:36
      at /usr/local/lib/node_modules/truffle/build/cli.bundled.js:326008:9
      at XMLHttpRequest.request.onreadystatechange (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:329052:7)
      at XMLHttpRequestEventTarget.dispatchEvent (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:176427:18)
      at XMLHttpRequest._setReadyState (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:176717:12)
      at XMLHttpRequest._onHttpResponseEnd (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:176872:12)
      at IncomingMessage.<anonymous> (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:176832:24)

In other words, when it gets to console.log(length), which should be the response of the addStruct function, it is instead getting { [String: '2'] s: 1, e: 0, c: [ 2 ] }, and then getStruct errors out with Error: Invalid JSON RPC response: {"id":21,"jsonrpc":"2.0"}

Any idea why this is happening? Both of these things mean nothing to me, so I don't even know what to Google. I've looked at the Truffle and Solidity documentation, and as far as I can tell, I am doing everything correctly.

Perhaps unrelated, but I'm including just in case it isn't -- previously, I had deployed this to Ganache successfully and was playing around in the browser console. I was able to call web3.eth.contract(abi) and get what appears to be the correct response, but when I added the address as an argument, it refused to give me anything except "invalid address." I confirmed in the web3 and ganache documentation that I was doing it right about 10 times, so I'm thinking that maybe something else is wrong. Or maybe not -- feel free to ignore this part.

1

In first scenario, your result is BigNumber, so you just need to add .toNumber() to length:

  it("should create a stuct", function() {
    return MyContract.deployed().then(function(instance) {
      return instance.addStruct.call(0xf17f52151EbEF6C7334FAD080c5704D77216b732, 1000);
    }).then(function(length) {
       console.log(length.toNumber());
       assert.equal(length.toNumber(), 1, "length is not 1");
    });
  });

Related: What are C, E and S properties in message call return object?


In second scenario I reworked your test a little bit:

it("get the struct that was previously created", function() {
    return MyContract.deployed().then(function(instance) {
      instance.addStruct.sendTransaction("0xf17f52151ebef6c7334fad080c5704d77216b732", 1000);
      return instance.getStruct.call(1);
    }).then(function(values) {
      console.log(values[0].toString(), values[1].toNumber());
      assert.equal(values[0].toString(), "0xf17f52151ebef6c7334fad080c5704d77216b732", 'blah-blah');
      assert.equal(values[1].toNumber(), 1000, 'blah-blah');
    });
  });

We will execute sendTransaction to actually add new struct to array. Then we will call it and returned array will be read with .toString() and .toNumber() to normalize output.


It is also good to note that if we call getStruct with 0 it returns 0x0000000000000000000000000000000000000000 0.

And the fix is:

function addStruct(address _a, uint _b) public returns (uint){
    MyStruct memory a = MyStruct(_a, _b);
    MyStructs.push(a);
    // delete this line (MyStructs.length++;)
    return MyStructs.length;
}

After this edit you can change call value from 0 back to 1 here:

return instance.getStruct.call(0);
1

@roman-frolov nailed the first issue.

For the second issue, I believe the problem is that you used call instead of sendTransaction when you invoked addStruct. Calls don't actually send transactions out to the network; they just run the function locally and then discard the side effects. So no struct was actually added to MyStructs. The subsequent call to getStruct will fail because it uses an invalid index into the array. (It tries index 0, but the array length is still 0.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.