You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TokenWeightedVote implements governance where influence is proportional to an address's allocated token balance — one token equals one vote. The owner allocates governance tokens to eligible voters before or during the voting period. Each token-holder may freely split their balance across as many proposals as they like by calling vote() multiple times.
Key concepts
Term
Meaning
Owner
The deploying account; can allocate tokens and close the ballot
Balance
Total governance tokens allocated to a voter by the owner
Spent
Tokens already committed to proposals
Remaining
balance - spent — tokens still available to vote with
Split vote
Calling vote() multiple times to spread tokens across proposals
Public interface
Owner functions
Function
Description
allocateTokens(address voter, uint256 amount)
Adds amount tokens to a voter's balance (additive; can be called multiple times)
closeVoting()
Permanently locks the ballot; no further allocations or votes accepted
Voter functions
Function
Description
vote(uint256 proposalIndex, uint256 weight)
Commits weight tokens to a proposal; may be called multiple times with different proposals
View functions
Function
Description
remainingTokens(address voter)
Unspent token balance available for voting
proposalCount()
Number of proposals in the ballot
winningProposalIndex()
Index of the leading proposal (ties favour the lower index)
In Hardhat v3, ethers is accessed via the network global that the console provides.
const{ ethers }=awaitnetwork.connect();
5 — Attach to the deployed contract and load signers
consttwv=awaitethers.getContractAt("TokenWeightedVote","0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512");// Hardhat pre-funds 20 accounts on the local node.// signers[0] is the owner (the account that deployed the contract).constsigners=awaitethers.getSigners();const[owner,alice,bob,carol]=signers;
6 — Allocate governance tokens
awaittwv.allocateTokens(alice.address,100);awaittwv.allocateTokens(bob.address,50);awaittwv.allocateTokens(carol.address,200);// Check total supplyawaittwv.totalSupply();// 350n// Check an individual balanceawaittwv.voters(alice.address);// { balance: 100n, spent: 0n }
7 — Cast votes (single proposal)
// Bob puts all 50 tokens on Proposal A (index 0)awaittwv.connect(bob).vote(0,50);
8 — Cast a split vote (multiple proposals)
// Alice spreads her 100 tokens across two proposalsawaittwv.connect(alice).vote(0,60);// 60 tokens → Proposal Aawaittwv.connect(alice).vote(1,40);// 40 tokens → Proposal B// Alice's remaining balance is now 0awaittwv.remainingTokens(alice.address);// 0n
9 — Check the running tally
// Proposal vote countsconst[nameA,countA]=awaittwv.proposals(0);console.log(`${nameA}: ${countA}`);// Proposal A: 110const[nameB,countB]=awaittwv.proposals(1);console.log(`${nameB}: ${countB}`);// Proposal B: 40// Current leaderawaittwv.winnerName();// 'Proposal A'
10 — Top up a voter's balance mid-poll
allocateTokens is additive, so you can call it again to give a voter more tokens while voting is still open.
awaittwv.allocateTokens(carol.address,100);// carol now has 300 totalawaittwv.voters(carol.address);// { balance: 300n, spent: 0n }// Carol votes for Proposal C with all 300 tokensawaittwv.connect(carol).vote(2,300);
11 — Close the ballot
awaittwv.closeVoting();awaittwv.votingClosed();// true// Final resultawaittwv.winnerName();// 'Proposal C'awaittwv.winningProposalIndex();// 2n
Common revert reasons
Error
Cause
NotOwner
Caller is not the owner
ZeroWeight
vote() called with weight = 0
InsufficientTokens
Requested weight exceeds the voter's remaining token balance