On-chain governance for the Upsilon (UPX) token — snapshot voting, quorum checks, timelock, and executable proposals through a React dashboard.
This DAO governs the pre-deployed upsilon token : UPX
| Token (Sepolia) | 0x57bfdA49355F95799399Deb4ff79aAB8d1971914 · Etherscan |
| Token repo | upsilon-token-UPX--ERC20-Create2 |
UPX (ERC20Votes) → voting power with checkpoints
UpsilonDAO → proposals, voting, queue, execute
- Delegate — UPX holders call
delegate()to activate voting power - Propose — accounts with ≥
proposalThresholdvotes submit proposals - Vote — weight is snapshotted at
voteStart; voting opens strictly after that block - Queue — successful proposals enter a timelock
- Execute — after the timelock, on-chain actions run (ETH transfers, contract calls, signaling with no actions)
| Parameter | Value |
|---|---|
| Token | Upsilon / UPX, 1M max supply |
proposalThreshold |
10 UPX |
quorumNumerator |
4 (4% of supply at snapshot) |
- Fixed max UPX supply; owner-only emergency mint capped at
MAX_SUPPLY - Proposal threshold reduces spam
- Percentage-based quorum at vote snapshot
- Timelock before execution
- Reentrancy guard on
execute - Custom errors for gas-efficient reverts
- Immutable governance parameters set at deploy
dao/
├── contracts/ # Foundry — UpsilonToken, UpsilonDAO, tests, deploy script
└── frontend/ # Vite + React UI (Wagmi / Viem)
cd contracts
forge build
forge testStart Anvil:
anvilDeploy:
cd contracts
forge script script/DeployUpsilon.s.sol \
--rpc-url http://127.0.0.1:8545 \
--broadcastCopy the logged UpsilonToken and UpsilonDAO addresses into frontend/.env:
VITE_CHAIN_ID=31337
VITE_TOKEN_ADDRESS=0x...
VITE_DAO_ADDRESS=0x...Create contracts/.env (do not commit):
PRIVATE_KEY=0x...Deploy:
cd contracts
source .env
forge script script/DeployUpsilon.s.sol \
--rpc-url https://sepolia.drpc.org \
--broadcast --private-key "$PRIVATE_KEY" \
--legacy --with-gas-price 20000000000 -vvPoint the frontend at Sepolia:
VITE_CHAIN_ID=11155111
VITE_TOKEN_ADDRESS=0x...
VITE_DAO_ADDRESS=0x...cd frontend
npm install
npm run devThe app reads governance parameters and proposal state from the chain pinned by VITE_CHAIN_ID — not from hard-coded UI assumptions.
Anvil — import a test account into MetaMask:
- RPC:
http://127.0.0.1:8545 - Chain ID:
31337
Sepolia — use a funded Sepolia account and the same chain ID as VITE_CHAIN_ID.
- Connect wallet
- Activate voting power (self-delegate UPX)
- Create a signaling or ETH transfer proposal
- Vote after the voting delay elapses
- After voting ends, Queue then Execute
For transfer proposals, fund the DAO treasury first:
cast send <DAO_ADDRESS> --value 1ether \
--rpc-url <RPC_URL> --private-key <KEY>