Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions wallet/entrypoints/popup/sheets-dao.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type ProposalRow = {
forVotes: number; againstVotes: number; abstainVotes: number;
blocksLeft: number | null; youVoted: boolean; yourWeight: number; actions: ProposalAction[];
};
type DaoStats = { treasuryLcai: number | null; quorumPct: number | null };
type DaoStats = { treasuryLcai: number | null; quorumPct: number | null; quorumLcai?: number | null; stakeExcluded?: boolean };
const SUPPORT_LABEL: Record<0 | 1 | 2, string> = { 1: "For", 0: "Against", 2: "Abstain" };
const STATE_TONE: Record<string, string> = { active: "tag-warn", succeeded: "tag-ok", executed: "tag-ok", queued: "tag-ok", defeated: "tag-bad", canceled: "tag-bad", expired: "tag-bad", pending: "" };

Expand Down Expand Up @@ -105,7 +105,7 @@ export function DaoSheet({ from, onClose }: { from: string; onClose: () => void
</div>
<div className="stat-grid" style={{ gridTemplateColumns: "1fr 1fr 1fr" }}>
<div className="stat"><div className="faint">Treasury</div><b>{stats == null ? "…" : stats.treasuryLcai == null ? "--" : `${fmtBal(String(stats.treasuryLcai))} LCAI`}</b></div>
<div className="stat"><div className="faint">Quorum</div><b>{stats == null ? "…" : stats.quorumPct == null ? "--" : `${stats.quorumPct}% of supply`}</b></div>
<div className="stat"><div className="faint">Quorum</div><b title={stats?.stakeExcluded ? `${stats.quorumPct ?? 3}% of the votable supply (staked LCAI excluded)` : undefined}>{stats == null ? "…" : stats.quorumLcai != null ? `${fmtBal(String(Math.round(stats.quorumLcai)))} LCAI` : stats.quorumPct == null ? "--" : `${stats.quorumPct}% of supply`}</b></div>
<div className="stat"><div className="faint">Your power</div><b>{power == null ? "…" : power.toLocaleString(undefined, { maximumFractionDigits: 2 })}</b></div>
</div>
<div className="chips">
Expand Down
26 changes: 24 additions & 2 deletions wallet/src/rpc/governance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { decodeDangerousCall } from "../provider/decode-call";

export const GOVERNORS: Record<number, `0x${string}`> = {
1: "0x6dfa413B5900a1a7947BC75E68AbBA093cB2492d",
9200: "0x262E9f9232933E8565253918db703baD58DE93aB",
// Live LightChainGovernor (verified on-chain; supersedes 0x262E9f).
9200: "0xD216A0c0050EdC3a9E0449EcFDf178A1652b4b68",
};

const PROPOSAL_CREATED = parseAbiItem(
Expand Down Expand Up @@ -223,10 +224,21 @@ const QUORUM_ABI = parseAbi([
"function quorumDenominator() view returns (uint256)",
]);
const BALANCE_ABI = parseAbi(["function balanceOf(address) view returns (uint256)"]);
// LightChain: the Governor's quorum() is overridden to use getTotalVotingPower
// (staked LCAI excluded), so the real quorum is 3% of the votable base, not 3%
// of raw supply. Read the resolved threshold directly.
const NATIVE_QUORUM_ABI = parseAbi([
"function quorum(uint256 timepoint) view returns (uint256)",
"function clock() view returns (uint48)",
]);

export interface DaoStatsView {
treasuryLcai: number | null; // null = unreadable right now
quorumPct: number | null;
// LightChain: the real quorum threshold in LCAI (3% of the staked-excluded
// votable supply) + a flag that staked LCAI is excluded from the base.
quorumLcai?: number | null;
stakeExcluded?: boolean;
}

/** Treasury holds the ERC-20 on Ethereum and native LCAI on LightChain. */
Expand All @@ -242,8 +254,18 @@ export async function readDaoStats(chainId: number): Promise<DaoStatsView> {
pub.readContract({ address: governor, abi: QUORUM_ABI, functionName: "quorumNumerator" }).catch(() => null) as Promise<bigint | null>,
pub.readContract({ address: governor, abi: QUORUM_ABI, functionName: "quorumDenominator" }).catch(() => 100n) as Promise<bigint | null>,
]);
return {
const base: DaoStatsView = {
treasuryLcai: bal === null ? null : Number(formatEther(bal)),
quorumPct: num === null || den === null || den === 0n ? null : Number((num * 10000n) / den) / 100,
};
// LightChain reports the REAL quorum: 3% of the staked-excluded votable supply.
if (chainId !== 9200) return base;
try {
const clock = BigInt(await pub.readContract({ address: governor, abi: NATIVE_QUORUM_ABI, functionName: "clock" }));
const ref = clock > 0n ? clock - 1n : 0n;
const quorumWei = (await pub.readContract({ address: governor, abi: NATIVE_QUORUM_ABI, functionName: "quorum", args: [ref] }).catch(() => null)) as bigint | null;
return { ...base, quorumLcai: quorumWei === null ? null : Number(formatEther(quorumWei)), stakeExcluded: true };
} catch {
return base;
}
}
Loading