Solidity – How to Get the Array Length in Ethers.js and Hardhat

ethers.jshardhatsolidity

I have a function that pushes a value into a public array.
Now in my hardhat tests, I would like to check that before the first time calling my function, the array has a length of 0, and afterward, it's 1.
However, somehow I always have to pass an index to the getter function.

Is there any way to get the length of a solidity array from ethers?

untested pseudo-code below:

Solidity

contract A {
  address[] public functionCaller;

  function test() external {
    functionCaller.push(msg.sender);
  }
}

tests/index.ts

// ...
await a.test()

// what to call here?
expect(await a.functionCaller().length).to.equal(1); // throws error

The error is:

Argument of type 'undefined' is not assignable to parameter of type 'BigNumberish'.

This makes sense since the function signature to get the array length seems to be different. Every help is appreciated. Thanks!

Best Answer

The length of a public array is not exposed by default in solidity.

You can either define a specific solidity function for it :

function getLength() public view returns (uint256) {
   return functionCaller.length;
}

And call that function in your test.

Or directly from ethers.js, read the storage slot of your array (in your example it's in storage slot 0) which holds its length (see the documentation). Be careful though, changing your contract's storage layout may change the storage slot of the array, thus, invalidating your test.

describe("Validate length on push", function () {
  it("Should increment the length after a push is done", async function () {
    const contract = await ethers.getContractFactory("A");
    const instance = await contract.deploy();
    await instance.deployed();

    // Read storage slot 0 holding the length of the array
    const initialLength = ethers.BigNumber.from(await ethers.provider.getStorageAt(instance.address, 0));

    expect(initialLength.eq(0)).to.equal(true);

    await instance.test();

    // Read storage slot 0 holding the length of the array
    const newLength = ethers.BigNumber.from(await ethers.provider.getStorageAt(instance.address, 0));

    expect(newLength.eq(1)).to.equal(true);
  });
});

I hope that answers your question.