Error trying to obtain set of fields from array of Solidity objects
I am trying to write a test for the voting example Ballot.sol here:
https://docs.soliditylang.org/en/v0.8.11/solidity-by-example.html
My test code looks like this :
const ethers = require('ethers');
const BallotContract = artifacts.require("Ballot");
contract("BallotPractice", accounts => {
let ballot;
const proposalNames = [ethers.utils.formatBytes32String("Proposal_1"),
ethers.utils.formatBytes32String("Proposal_2"),
ethers.utils.formatBytes32String("Proposal_3")];
describe("initialization", () => {
beforeEach (async () => {
ballot = await BallotContract.new(proposalNames);
});
it("gets the proposalNames", async () => {
let agendas = ballot.proposals.map(({item}) => item.name);
const actual = await agendas;
assert.equal(actual, proposalNames, "proposalNames should match");
});
});
});
I got this error when I ran truffle test:
TypeError: ballot.proposals.map is not a function
I'm still fairly new to Solidity so would appreciate your help.
I'm trying to obtain the list of proposal names from ballot.proposals.
Thanks in advance.
The Ballot.sol code is as follows:
pragma solidity >=0.7.0 < 0.9.0;
/// @title Voting with delegation
contract Ballot {
// Declare new complex tye to be used for variables later.
// Represents single voter
struct Voter {
uint weight; // weight can be accumulated by delegation
bool voted; // true if person has already voted
address delegate; // person this voter has delegated their vote to
uint vote; // index of the voted proposal
}
// Type for a single proposal
struct Proposal {
bytes32 name; // short name
uint voteCount; // number of accumulated votes
}
address public chairperson;
/*
* Declare a state variable that
* stores a Voter struct for each
* possible address.
*/
mapping(address => Voter) public voters;
// Dynamically sized array of Proposal structs
Proposal[] public proposals;
// Create new ballot to choose one of proposalNames
constructor(bytes32[] memory proposalNames) {
chairperson = msg.sender;
voters[chairperson].weight = 1;
/*
* For each of the provided proposal names,
* create a new proposal and add it
* to the end of the array
*/
for (uint i=0; i < proposalNames.length; i++) {
// Create Proposal object and append to proposals array
proposals.push(Proposal({
name: proposalNames[i],
voteCount:0
}));
}
}
/*
* Give voters the right to vote on this ballot.
* May only be called by the chairperson
*/
function getRightToVote(address voter) external {
require(
msg.sender == chairperson,
"Only chairperson can grant right to vote"
);
require(
!voters[voter].voted,
"Voter already voted"
);
require(voters[voter].weight==0);
voters[voter].weight = 1;
}
// Delegate your vote to the voter to
function delegate(address to) external {
// assign reference
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted");
require(to != msg.sender, "Self-delegation disallowed");
/* Walk along the path of potential delegates until the address is 0.
* This is to handle multiple levels of delegation
*/
while(voters[to].delegate != address(0)) {
to = voters[to].delegate;
// Loop in delegate path not allowed
require(to != msg.sender, "Loop in delegation encountered");
}
sender.voted = true;
sender.delegate = to;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
/* If delegate already voted,
* add to number of votes
*/
proposals[delegate_.vote].voteCount += sender.weight;
} else {
//If the delegate did not vote yet, add to her weight
delegate_.weight += sender.weight;
}
}
// vote function. Give your vote to a specific proposal
function vote(uint proposal) external {
Voter storage sender = voters[msg.sender];
require(sender.weight !=0, "Has no right to vote");
require(!sender.voted, "Already voted");
sender.voted = true;
sender.vote = proposal;
proposals[proposal].voteCount += sender.weight;
}
// Compute winning proposal taking all previous votes into account
function winningProposal() public view
returns (uint winningProposal_) {
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++ ) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}
/* Call winningProposal() function to get index of winner
contained in proposals array and return the name of the winner
*/
function winnerName() external view returns (bytes32 winnerName_) {
winnerName_ = proposals[winningProposal()].name;
}
function getProposalsLength() external view returns(uint){
return proposals.length;
}
function getProposalByIndex(uint index) external view returns (bytes32){
return proposals[index].name;
}
}
Solution 1:
this call is wrong:
let agendas = ballot.proposals.map(({item}) => item.name);
-
if you are working with contract always use
await
-
I assume
proposals
is a method in contract. this is how you should call a contract method if it is not modifying statelet agendas =await ballot.proposals.call()
-
Looks like you want to return an array from the contract directly, but you cannot return array. If
proposals
is returning an array from contract, i belive it will return the address of the array not array with items. this is how you should return array from the contract
Array returned from Solidity assigned as bigNumber in Javascript