> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kas.fun/llms.txt
> Use this file to discover all available pages before exploring further.

# 选举

> Council 选举的链上工作原理

## 概述

Council 选举允许代币持有者为候选人投票。得票最多的候选人成为 Council 成员。投票需要**锁定代币**到 Council 合约中。

## 选举流程

<Steps>
  <Step title="选举开始">
    选举被发起（需要平台的 EIP-712 签名）。选举有固定的持续时间。
  </Step>

  <Step title="投票期">
    代币持有者授权代币给 Council 合约，然后调用 `voteElection()` 为候选人投票。代币在选举结束前一直锁定。
  </Step>

  <Step title="选举结束">
    `endTime` 过后，任何人都可以调用 `claimElectionTokens()`。第一个调用者触发结算——得票最高的候选人成为 Council 成员。
  </Step>

  <Step title="领回代币">
    所有投票者调用 `claimElectionTokens()` 取回锁定的代币。
  </Step>
</Steps>

## 读取选举状态

```javascript theme={null}
const council = new ethers.Contract(councilAddress, CouncilV1ABI, provider);

const state = await council.getElectionState(tokenAddress);

console.log("进行中:", state.active);
console.log("结束时间:", new Date(Number(state.endTime) * 1000));
console.log("已结算:", state.finalized);
console.log("席位数:", state.councilSize.toString());
console.log("届期:", state.epoch.toString());
```

### 选举状态字段

| 字段                | 类型      | 说明              |
| ----------------- | ------- | --------------- |
| `active`          | bool    | 选举是否正在进行        |
| `endTime`         | uint256 | 选举结束时间戳（unix 秒） |
| `lastElectionEnd` | uint256 | 上一次选举结束时间       |
| `finalized`       | bool    | 当前/上次选举结果是否已结算  |
| `councilSize`     | uint256 | Council 席位数     |
| `epoch`           | uint256 | 选举届期计数器         |

## 在选举中投票

<Warning>
  投票前，必须先 **approve** Council 合约转移你的代币。
</Warning>

```javascript theme={null}
const tokenContract = new ethers.Contract(tokenAddress, [
  "function approve(address spender, uint256 amount) returns (bool)",
], signer);

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

// 1. 授权代币
const amount = ethers.parseEther("10000");
await (await tokenContract.approve(councilAddress, amount)).wait();

// 2. 为候选人投票
// 可以将代币分配给多个候选人
const votes = [
  { candidate: "0xCandidate1...", amount: ethers.parseEther("6000") },
  { candidate: "0xCandidate2...", amount: ethers.parseEther("4000") },
];

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

## 查看候选人

```javascript theme={null}
// 获取前 20 名候选人，按得票数降序排列
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])} 票`);
}

// 查询特定候选人的得票数
const votes = await council.getCandidateVotes(tokenAddress, candidateAddress);
```

## 选举结束后领回代币

选举结束后（`block.timestamp > endTime`）：

```javascript theme={null}
// 第一个调用者触发结算 + 领回
// 后续调用者直接领回代币
const tx = await council.claimElectionTokens(tokenAddress);
await tx.wait();
```

## 查看 Council 成员

```javascript theme={null}
// 选举结算后
const members = await council.getCouncilMembers(tokenAddress);
console.log("Council 成员:", members);

// 检查某地址是否为 Council 成员
const isMember = await council.isCouncilMember(tokenAddress, someAddress);
```

## 事件

| 事件                                                              | 说明        |
| --------------------------------------------------------------- | --------- |
| `ElectionStarted(token, endTime, councilSize)`                  | 选举开始      |
| `ElectionVoted(token, voter, totalLocked, candidates, amounts)` | 投出选票      |
| `ElectionFinalized(token, councilMembers)`                      | 选举结果结算    |
| `ElectionTokensClaimed(token, voter, amount)`                   | 投票者领回锁定代币 |
