> ## 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 成员可以创建提案。所有代币持有者可以通过锁定代币对提案投票。投票期结束后，结果被结算，胜出选项被确定。

## 治理周期

治理以周期运作，分为两个阶段：

```mermaid theme={null}
graph LR
    A["提案阶段<br/>Council 创建提案"] --> B["投票阶段<br/>代币持有者投票"]
    B --> C["已结算<br/>确定结果"]
```

### 读取治理状态

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

const state = await council.getCouncilState(tokenAddress);

console.log("阶段:", state.governancePhase); // 0=空闲, 1=提案, 2=投票
console.log("提案阶段截止:", new Date(Number(state.proposalPhaseEnd) * 1000));
console.log("投票阶段截止:", new Date(Number(state.votingPhaseEnd) * 1000));
console.log("当前提案 ID:", state.currentProposalId.toString());
console.log("国库:", ethers.formatEther(state.treasuryBalance));
```

## 对提案投票

<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. 对提案投票
// target: 通用提案使用 address(0)
// optionIndex: 你要投票的选项索引
// amount: 分配给该选项的代币数量
const votes = [
  {
    target: ethers.ZeroAddress,
    optionIndex: 0,  // 第一个选项
    amount: ethers.parseEther("10000"),
  },
];

const proposalId = 1; // 链上提案 ID
const tx = await council.voteProposal(tokenAddress, proposalId, votes);
await tx.wait();
```

<Tip>
  你可以在一次交易中将代币分配给多个选项。
</Tip>

## 查看提案详情

```javascript theme={null}
// 获取提案信息
const proposal = await council.getProposal(tokenAddress, proposalId);

console.log("开始:", new Date(Number(proposal.startTime) * 1000));
console.log("结束:", new Date(Number(proposal.endTime) * 1000));
console.log("已结算:", proposal.finalized);
console.log("类型:", proposal.proposalType); // 0=通用, 1=税费
console.log("选项:", proposal.optionCodes);
console.log("胜出选项:", proposal.winningOption.toString());

// 获取每个选项的得票数
const optionVotes = await council.getProposalOptionVotes(tokenAddress, proposalId);
optionVotes.forEach((votes, i) => {
  console.log(`选项 ${i}: ${ethers.formatEther(votes)} 票`);
});

// 获取自己的投票
const myVotes = await council.getVoterOptionVotes(tokenAddress, proposalId, myAddress);

// 查看锁定金额（提案结束后可领回）
const locked = await council.getVoterLockedAmount(tokenAddress, proposalId, myAddress);
```

## 投票结束后领回代币

投票期结束后：

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

## 税费配置

税费提案设置代币的交易税。税费提案结算并应用胜出选项后：

```javascript theme={null}
const taxConfig = await council.getTaxConfig(tokenAddress);

console.log("税率:", taxConfig.rate.toString()); // 基点
console.log("用途:", taxConfig.purpose); // 0=无, 1=销毁, 2=转账, 3=国库
console.log("目标:", taxConfig.target);

// 检查地址是否免税
const exempt = await council.isTaxExempt(tokenAddress, someAddress);
```

| 用途              | 值 | 说明                |
| --------------- | - | ----------------- |
| None            | 0 | 无税                |
| Burn            | 1 | 税收代币被销毁           |
| Transfer        | 2 | 税收代币发送到指定地址       |
| CouncilTreasury | 3 | 税收代币进入 Council 国库 |

## 事件

| 事件                                                                                        | 说明   |
| ----------------------------------------------------------------------------------------- | ---- |
| `ProposalCreated(token, proposalId, nonce, proposer, proposalType, optionCodes, endTime)` | 提案创建 |
| `ProposalFinalized(token, proposalId, winningOption, winningVotes)`                       | 提案结算 |
