lux.vote was served by an interface derived from Decent DAO under AGPL-3.0. This is a fresh app on @hanzo/gui with no Decent lineage — the replaced app's route table was read to enumerate what the interface must do, and nothing else. No Tailwind anywhere. @luxfi/ui is not a dependency: its prebuilt components carry arbitrary-utility class strings and are the only reason the exchange scans for them. Dropping it means replacing two things it provided — createGui, which GuiProvider alone does not call and without which the tree throws and the document is blank, and luxUi()'s aliases, now read from node_modules/@hanzogui at config time so an upgrade cannot leave a package undeduplicated. Twelve screens over the Governor, the votes token, the Timelock, the work market, Roles, Karma and the gauges, wired to luxfi/standard's Foundry artifacts rather than to its stale ABI barrel. The contracts are not what the prose says: Bounty has no escrow, arbiter or dispute state; MultiDAOGovernor swaps Executed and Expired; Karma emits no Transfer so no holder list exists. The Governor's clock is mode=timestamp, which is asked for rather than assumed because 96369 produces blocks on demand and reading seconds as blocks turns one day into months. Every read reports one of four outcomes — deployed, not deployed, not read, no record. A contract with no code answers with empty data rather than an error, so an absent contract and an idle one arrive identically at the call site; only one of them is a statement about governance. /deployment asks eth_getCode for every address on every chain on load, because the written records disagree with the chain and with each other. Endpoints are the /v1/bc/C/rpc form. The bare host serves the same chain to curl and refuses a browser — 405 on preflight, no allow-origin header — which would report every Governor unreadable while the Governor was fine. Wallet login is @luxwallet/connect, imported by subpath: its root barrel pulls a connector factory naming all seven chains, and a bundler follows every branch into optional peers the dev server never resolves. It signs a CAIP-122 message and offers no provider, transactions or chain switching, so viem is built over our own EIP-6963 discovery for the calls that write. Telemetry off: the design system posts to api.hanzo.ai on load by default, and who reads a proposal is not this interface's to report. Gates ported and made real. The class check ran against the Suspense fallback until a negative control caught it passing on a route with an injected utility class; every gate now waits for the screen's own h1. 22 unit tests, 135 browser tests against the built output. Co-authored-by: Hanzo Dev <[email protected]>
2.3 KiB
vote
The governance interface for lux.vote, built on @hanzo/gui.
Every figure on every screen is read from the chain when the screen opens. Nothing is seeded, cached or illustrative, and the interface holds no key — you sign every transaction in your own wallet.
Running it
pnpm install
pnpm dev # http://127.0.0.1:5288
pnpm verify # typecheck, unit tests, build, then the browser suite against the build
pnpm verify is the gate. It runs the browser suite against the built
output rather than the dev server, because a suite that never bundles says
nothing about whether the thing ships — a missing export in an optional peer
took this build down while the dev server stayed green.
Screens
| Route | What it reads |
|---|---|
/ |
Governor parameters, the proposal count, and whether anyone can vote |
/proposals |
Every ProposalCreated log, with each proposal's live state and tally |
/proposals/:id |
One proposal, its window and its votes; casts a vote |
/proposals/new |
Opens a proposal, after saying what it takes to open one |
/delegate |
Balance against voting power, delegation, and the vote-escrow lock |
/treasury |
Timelock delay and roles, and what the treasury contracts hold |
/work |
The bounty board and the contribution ledger |
/roles |
The role registry — ids, parents, wearer counts |
/karma |
Soulbound reputation: supply, cap, and your own standing |
/gauges |
Fee direction, weighted by vote-escrow |
/deployment |
Every address on record, on all four chains, asked of its own chain |
/settings |
Governance parameters, the endpoint, theme and type |
The rule
Four readings, never two:
- deployed — code was found at the address
- not deployed — the chain answered and there is no code there
- not read — the call was refused or timed out; nothing is known either way
- no record — no file names an address for this contract on this chain
A contract with no code answers a call with empty data rather than an error, so
an interface that does not ask the question cannot tell an absent contract from
an idle one. /deployment asks it for every address, on every chain, on load.
e2e/honesty.spec.ts enforces this against the rendered page.
Licence
Lux Ecosystem Licence. See LICENSE.