Search code examples
ethereumsoliditysmartcontracts

How can I make hardhat access contract's public array field


I have a contract in solidity which includes an array field.

ragma solidity ^0.8.9;

contract Ballot {

  struct Proposal {
    string name;
    uint voteCount;
  }


  Proposal[] public proposals;

  constructor(string[] memory proposalNames) {
    
    for (uint i = 0; i < proposalNames.length; i++) {
      proposals.push(Proposal({
        name: proposalNames[i],
        voteCount: 0
      }));
    }
  }

I am trying to write unit test in hardhat but it has compile error about accessing proposals field:

describe('Ballot', function () {
  async function deployFixture() {
    const [owner, otherAccount] = await ethers.getSigners();

    const Lock = await ethers.getContractFactory('Ballot');
    const names = [v4(), v4(), v4(), v4()];
    const ballot = await Lock.deploy(names);

    return { ballot, names, owner, otherAccount };
  }

  describe('Deployment', function () {
    it('Should set the right name', async function () {
      const { ballot, names } = await loadFixture(deployFixture);
      expect(await ballot.proposals()).to.equal(names);
    });

The call ballot.proposals() has an error:

Argument of type '[]' is not assignable to parameter of type 'ContractMethodArgs<[arg0: BigNumberish], "view">'.
  Type '[]' is not assignable to type '[arg0: Typed | BigNumberish, ViewOverrides]'.
    Source has 0 element(s) but target requires 

it is an array of struct, how can I access it from unit test? I have tested that it works fine for a string field.


Solution

  • Your assumption is partly correct - public property autogenerates a getter function. But in case of an array, the getter function accepts an uint256 argument with the index of the array that you want to access.

    expect(await ballot.proposals(0)).to.equal(names[0]);
    

    From the docs page:

    If you have a public state variable of array type, then you can only retrieve single elements of the array via the generated getter function. This mechanism exists to avoid high gas costs when returning an entire array.


    If you want to retrieve the whole array, you need to create a custom function in the contract that returns the whole array.

    function getAllProposals() external view returns (Proposal[] memory) {
        return proposals;
    }
    
    expect(await ballot.getAllProposals()).to.equal(names);
    

    Note: Your JS names are an array of strings but the Solidity proposals are array of structs/objects. Make sure you're comparing the same values (e.g. filter only the name without voteCount from the struct).