1

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!

1
  • try the following: const tx = await a.test(); await tx.wait(); const arr = await a.functionCaller(); expect(arr.length).to.equal(1);
    – Majd TL
    Commented Apr 27, 2022 at 8:29

2 Answers 2

2

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.

0

If you have access to the contract, you can easily get the length of an array storage variable with ethers.provider.getStorageAt(addr, index) [reference]

You just have to know which index is the variable initialized, i.e.

contract Sample {
    address public owner;                     // index 0
    uint256 public constant HELLO = "hello";  // NOT INDEXED!
    address[] public holders;                 // index 1
}

For more of this, you can check fcc's video and solidity docs

If you want to get the length of the holders variable, then

// only the length of `holders` is stored in index 1 and not the actual array
const bytes32Length = await ethers.provider.getStorageAt(sample.address, 1)

// convert bytes32Length to int
const length = parseInt(bytes32Length, 16)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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