Skip to main content

Overview

Council elections allow token holders to vote for candidates. The top candidates (by vote amount) become council members. Voting requires locking tokens in the Council contract.

Election Flow

1

Election Starts

An election is initiated (requires EIP-712 signature from the platform). The election has a fixed duration.
2

Voting Period

Token holders approve their tokens to the Council contract, then call voteElection() to vote for candidates. Tokens are locked until the election ends.
3

Election Ends

After endTime, anyone can call claimElectionTokens(). The first caller triggers finalization — top candidates become council members.
4

Claim Tokens

All voters call claimElectionTokens() to retrieve their locked tokens.

Reading Election State

const council = new ethers.Contract(councilAddress, CouncilV1ABI, provider);

const state = await council.getElectionState(tokenAddress);

console.log("Active:", state.active);
console.log("End time:", new Date(Number(state.endTime) * 1000));
console.log("Finalized:", state.finalized);
console.log("Council size:", state.councilSize.toString());
console.log("Epoch:", state.epoch.toString());

Election State Fields

FieldTypeDescription
activeboolWhether an election is currently running
endTimeuint256Election end timestamp (unix seconds)
lastElectionEnduint256When the last election ended
finalizedboolWhether the current/last election results are finalized
councilSizeuint256Number of council seats
epochuint256Election epoch counter

Voting in an Election

Before voting, you must approve the Council contract to transfer your tokens.
const tokenContract = new ethers.Contract(tokenAddress, [
  "function approve(address spender, uint256 amount) returns (bool)",
], signer);

const council = new ethers.Contract(councilAddress, CouncilV1ABI, signer);

// 1. Approve tokens
const amount = ethers.parseEther("10000");
await (await tokenContract.approve(councilAddress, amount)).wait();

// 2. Vote for candidates
// You can split your tokens across multiple candidates
const votes = [
  { candidate: "0xCandidate1...", amount: ethers.parseEther("6000") },
  { candidate: "0xCandidate2...", amount: ethers.parseEther("4000") },
];

const tx = await council.voteElection(tokenAddress, votes);
await tx.wait();

Checking Candidates

// Get top 20 candidates sorted by votes (descending)
const [candidates, voteAmounts] = await council.getTop20Candidates(tokenAddress);

for (let i = 0; i < candidates.length; i++) {
  if (candidates[i] === ethers.ZeroAddress) break;
  console.log(`${candidates[i]}: ${ethers.formatEther(voteAmounts[i])} votes`);
}

// Check a specific candidate's votes
const votes = await council.getCandidateVotes(tokenAddress, candidateAddress);

Claiming Tokens After Election

After the election ends (block.timestamp > endTime):
// First caller triggers finalization + claims
// Subsequent callers just claim their tokens
const tx = await council.claimElectionTokens(tokenAddress);
await tx.wait();

Reading Council Members

// After election is finalized
const members = await council.getCouncilMembers(tokenAddress);
console.log("Council members:", members);

// Check if an address is a council member
const isMember = await council.isCouncilMember(tokenAddress, someAddress);

Events

EventDescription
ElectionStarted(token, endTime, councilSize)Election begins
ElectionVoted(token, voter, totalLocked, candidates, amounts)A vote is cast
ElectionFinalized(token, councilMembers)Election results finalized
ElectionTokensClaimed(token, voter, amount)Voter claims locked tokens