1

I have a simple contract that sets an owner when the contract is initialized, but am having trouble understanding why it changes under certain circumstances.

I tried 3 ways of initializing and setting an array:

  1. Using an array in memory
  2. Defining a storage array outside of a method and pushing to it
  3. Defining a storage array within a method and pushing to it

Cases 1 and 2 behave as expected, but Case 3 ends up changing the owner value and causes tests to fail. Can anyone provide insight on this behavior?

Smart contract:

pragma solidity ^0.4.4;

contract WeirdCase {

    address internal owner;
    uint256[] newArray;

    constructor() {
        owner = msg.sender;
    }

    function someMethod1() external {
        uint256[] memory newArray = new uint256[](10);
        for (uint256 i = 0; i < 10; i++) {
            newArray[i] = i;
        }
    }

    function someMethod2() external {
        for (uint256 i = 0; i < 10; i++) {
            newArray.push(i);
        }
    }

    function someMethod3() external {
        uint256[] storage weirdArray;
        for (uint256 i = 0; i < 10; i++) {
            weirdArray.push(i);
        }
    }

    function getOwner() view returns(address) {
        return owner;
    }
}

Test case:

var WeirdCase = artifacts.require("./WeirdCase.sol");

contract('WeirdCase', function(accounts) {

  let weirdCase;

  beforeEach(async () => {
    weirdCase = await WeirdCase.new();
  });


  it("Case 1: succeeds", async function () {
    let beforeOwner = await weirdCase.getOwner();
    await weirdCase.someMethod1();

    let afterOwner = await weirdCase.getOwner();
    assert.equal(beforeOwner, afterOwner);
  });

  it("Case 2: succeeds", async function () {
    let beforeOwner = await weirdCase.getOwner();
    await weirdCase.someMethod2();

    let afterOwner = await weirdCase.getOwner();
    assert.equal(beforeOwner, afterOwner);
  });

  /*
   * DON'T KNOW WHY THIS FAILS!!!
   */
  it("Case 3: not sure why this fails", async function () {
    let beforeOwner = await weirdCase.getOwner();
    await weirdCase.someMethod3();

    let afterOwner = await weirdCase.getOwner();
    assert.equal(beforeOwner, afterOwner);
  });
});
0

This line is the key:

uint256[] storage weirdArray;

You're defining weirdArray as a pointer to an array in storage, but you never point it to any array in storage. Therefore, the weirdArray array pointer is initialized as 0x00.

This causes the .push() to write to storage location 0x00, because the push method thinks that that's where the array is stored. 0x00 happens to be where the compiler placed your owner state variable.

  • That makes sense... I guess i need to rethink how to when/how to use dynamic arrays or arrays in memory. Thanks a bunch! – cryptoninja May 4 '18 at 8:42

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.