bddbd1359a | ||
---|---|---|
.. | ||
abi | ||
contracts | ||
deploy | ||
test | ||
.eslintrc.json | ||
.solhint.json | ||
README.md | ||
package.json | ||
prettier.config.js | ||
tsconfig.json | ||
waffle.config.json |
README.md
wakuconnect-voting-sdk/contracts
WakuConnect Proposal Contracts.
Voting Contract
The Voting Contract is a smart contract that enables the storage of proposals, from creation to vote result.
Lifecycle of proposal:
- Initialize proposal,
- Proposal is open, votes are accepted,
- Voting time ends, votes are no longer accepted, result is final.
Types
The main types used in the contract are:
-
Proposal
Holds information about the proposal
Fields
// block at which the proposal was created uint256 startBlock; // timestamp in seconds after which new votes won't be accepted // when casting votes endAt is compared to block.timestamp uint256 endAt; // proposal title string title; // proposal description string description; // total number of votes in favour of the proposal, this may be weighted uint256 totalVotesFor; // total number of votes against the proposal, this may be weighted uint256 totalVotesAgainst; // list of addresses that already voted address[] voters;
-
Vote
Holds the information for one vote
Fields
// address of the voter address voter; // encoded proposalId and type // first bit this field is a vote type: // 1 is a vote for // 0 is a vote against // rest of this field is a roomId shifted one bit to // the left uint256 proposalIdAndType; // amount of token used to vote uint256 tokenAmount; //signature of vote bytes32 r; bytes32 vs;
Constants and variables
-
token
contract address of the token used for vote verification. It is assigned at contract creation. Only holders of this token can vote. -
voteDuration
Length of duration during which proposals can be vote on, in seconds. It is assigned at contract creation. -
EIP712DOMAIN_TYPEHASH
Constant holding type hash of EIP712 domain as per EIP712 specification. -
VOTE_TYPEHASH
Constant holding type hash of Vote as per EIP712 specification. -
DOMAIN_SEPARATOR
Variable holding hash of domain separator according to EIP712 spec. It is assigned at smart contract creation. -
voted
Holds whether a given address has voted to a given proposal. It is a mapping of proposal id to mapping of addresses to booleans which indicates whether the given address has voted. -
proposals
Array that holds all proposals. The id for a proposal is the index in this array.
Signing with EIP712
EIP712 MUST be used to sign votes. The structure of typed data for a vote message is as follows:
{
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Vote: [
{ name: 'proposalIdAndType', type: 'uint256' },
{ name: 'tokenAmount', type: 'uint256' },
{ name: 'voter', type: 'address' },
],
},
primaryType: 'Vote',
domain: {
name: 'Voting Contract',
version: '1',
chainId: chainId,
verifyingContract: contract.address,
},
message: {
voter: voterAddress,
tokenAmount: tokenAmount,
proposalIdAndType: proposalIdAndType
}
}
For more information about EIP-712 go to docs
Functions
-
constructor(IERC20 _address)
Assigns_address
totoken
and generatesDOMAIN_SEPARATOR
-
getProposals()
Returns proposals -
getProposalsLength()
Returns the length of theproposals
array -
getLastNProposals(uint256 amount)
Returns the N last proposals -
getProposalFromId(uint256 id)
Gets proposal from given id -
getOpenProposals()
Returns proposals for whichproposal.endAt > block.timestamp
which means the proposals are still accepting votes. SinceproposalDuration
is set at contract creation and never changed,proposal.endAt
is never decreasing with increasing index of votingRoom. Therefore, it is enough to check fromproposal.length
up to first element whichendAt < block.timestamp
-
listVotersForProposal(uint256 proposalId)
Returns a list of voters for a given proposal. Reverts if there are no proposal for the givenproposalId
. -
initializeProposal(string calldata title, string calldata description, uint256 creatorVoteForAmount)
Creates a new proposal with the vote of the proposal creator. First checks if the creator voter has enough tokens to set vote for. Then creates a new proposal.startBlock
is set as current block number.endAt
is set a current block timestamp plusvotingDuration
.title
is set as argumenttitle
.description
is set as argumentdescription
.creatorVoteForAmount
is set as argumentvoteAmount
. Mappingvoted
of new proposal id ofmsg.sender
is set to true to reflect that message sender has voted on this voting room withcreatorVoteForAmount
.votingRooms
are appended with newVotingRoom andvoters
in this new appended element are appended with message sender. After proposal creation,ProposalStarted
is emitted. -
verify(Vote calldata vote, bytes32 r, bytes32 vs)
Function used to verify thatvote
was signed byvote.voter
as per EIP712 specification. See docs for more info. -
castVote(Vote calldata vote, uint256 proposalId)
Cast a single vote. UpdatestotalVotes
amount of proposal with index corresponding toproposalId
.If voting first bit of
vote.proposalIdAndType
is 1 that means that vote is for andvote.tokenAmount
is added tovotingRooms[roomId].totalVotesFor
, otherwise ifvote.proposalIdAndType
is 0vote.tokenAmount
is added tovotingRooms[roomId].totalVotesAgainst
.After that add new address to room
voters
and updates mappingvoted
accordingly. -
castVotes(Vote[] calldata votes)
Function used to cast votes on a single proposas. Function accepts an array of votes of typeVote
.All votes are looped through and verified that votes are:
- properly signed
- voter has enough tokens to vote
- proposal exists
- proposal hasn't been closed
Vote verification is as follows:
First
proposalId
is decoded fromvote.proposalIdAndType
which means shifting it to the right once.Then it is verified that the proposal with given
proposalId
exists and isn't closed, otherwise, function reverts.Then it is checked that
vote.voter
didn't vote in this vote room before if he did function goes to another voter (IDEA: emit alreadyVoted ?).After that it is verified that
vote
has been signed byvote.voter
.Last check is whether
vote.voter
has enough tokens to vote.If all checks passed, the vote is cast with
castVote
andVoteCast
is emitted.