1

If a struct contains a single field, then that field is inaccessible from web3. In the below example, field uint b must also be present in order to access field uint c from web3. Of course, it's not the best use of structs if you're only storing a single field, but this unexpected behavior is also present in the more realistic scenario of using any number of additional array or mapping fields.

Workaround for now is to add unused dummy fields to my structs, but I'm wondering if this is expected behavior, and if this quirk is noted anywhere in the solidity documentation? I didn't find anything mentioned in the structs section.

Contract code:

pragma solidity ^0.5.0;

contract StructAccess {

    struct SimpleStruct {
        //uint b; // c inaccessible if this is commented-out.
        uint c;
        uint[] d; // This has no impact on ability to access c.
        mapping (uint => uint) e; // This has no impact on ability to access c.

    }

    SimpleStruct public singleStruct;

    constructor() public {
        singleStruct.c = 5;
    }
}

Testing code:

const StructAccess = artifacts.require("StructAccess");

contract('StructAccess', (accounts) => {

  it('should pass struct access test', async () => {
    const accessInstance = await StructAccess.deployed();

    const singleStruct = await accessInstance.singleStruct();

    console.log(singleStruct.c);
    assert.equal(singleStruct.c, 5);
  });

});

Test results:

With uint b, singleStruct.c is 5
Without uint b, singleStruct.c is undefined

Version info:

> truffle version
Truffle v5.0.13 (core: 5.0.13)
Solidity v0.5.0 (solc-js)
Node v8.11.2
Web3.js v1.0.0-beta.37
1

You are in the vicinity of a known "gotcha" but the symptoms are not exactly like you report.

Here it is in Remix with uint b commented out, and present to show it works in both cases.

enter image description here

You will run into problems if both scaler variables are commented out. That would leave the struct with only indexed variables, the array and the mapping.

That situation is perfectly fine as a storage structure but note that the array and mapping are not included in the "free" getter provided by public. This reflects a limitation of the compiler. It doesn't know precisely how you would want the function constructed so it leaves them out. If there isn't at least one scaler variable, then that implies there would be nothing to return. Consequently, you cannot use public with a data structure that contains only indexed variables. More info: Compiler Issue or Improper State Variable declaration?

On the client side, since more than one value is returned in the case that uint b is uncommented, you will get an array instead of a scaler value. You should inspect singleStruct[0] and singleStruct[1] to see the two values. I haven't tested that, but hopefully, it nudges you in the right direction.

Hope it helps.

  • I should have emphasized the test results between the two cases. Without member uint b, singleStruct.c is undefined, otherwise the uint c member can be correctly accessed by name. Edited my original post to include this detail. Since member uint c is listed in Remix with uint b commented, does this suggest this is an "access member by name issue" in truffle / web3js? Is there a more reliable way to access structure members by name in truffle tests? – MilesF Apr 21 at 19:24
  • What do you get when you console.log(singleStruct)? – Rob Hitchens Apr 21 at 19:48
  • With uint b, console.log(singleStruct) produces Result { '0': <BN: 0>, '1': <BN: 5>, b: <BN: 0>, c: <BN: 5> }. Without uint b, and just uint c, only <BN: 5> is logged. The output is the same with or without the empty array and mapping. – MilesF Apr 21 at 20:41
  • So, I erred in the syntax, but you can see that one is an array and the other is a simple variable, so the syntax you use to unpack the response is different. – Rob Hitchens Apr 21 at 21:15
  • It seems that the main issue here is that field names are only preserved when structs contain multiple scalars, and that this is only a problem for web3js and not Remix. I'm accepting this answer because you provided helpful troubleshooting advice, but I still have remaining questions about the underlying reason for this behavior. Pursuing further in this thread. – MilesF Apr 25 at 17:20

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.