Files
universe/e2e/30-dao-onchain.spec.ts
zeekayandHanzo Dev 50646b4298 e2e: assert what 96369 says about the DAO stack
The Lux block asserted three Safe addresses had code — all three are
empty — and asserted a Governor was dead, at an address that is not the
Governor. Two tests failed and the third passed about nothing, so the
suite that exists to catch this could not.

It now reads the live Governor: deployed, counting in seconds, voting
the token deployed with it. The clock assertion is the one worth having
— the deploy was fine and unusable, because OpenZeppelin's default
block-number clock turned votingDelay/votingPeriod written as seconds
into block counts, which on a demand-driven chain also stops whenever
the chain is quiet. Only the mode says which unit those numbers are in.

The Safe absence is asserted rather than skipped, over all four
addresses the various records name. The day one has code this fails, and
that failure is the reminder to point lux/dao at it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-29 22:58:59 -07:00

240 lines
12 KiB
TypeScript

import { test, expect } from '@playwright/test'
import {
CHAIN_IDS,
orgRpc,
ethChainId,
ethCall, ethGetCode, hasCode, decodeAddress, encodeUint,
} from './helpers'
// ---------------------------------------------------------------------------
// DAO work-market — genuine on-chain deployment + wiring, per live org
// ---------------------------------------------------------------------------
// Asserts the DAO/governance/work-market stack is REALLY deployed and wired on
// each org's sovereign C-Chain — not merely that an RPC answers. Every fact here
// is read straight off-chain via eth_call / eth_getCode.
//
// Ground truth (deployment records — do not invent addresses):
// Zoo 200200 → lux/dao/contracts/deployments/lux-dao/200200.json
// Pars 494949 → lux/dao/contracts/deployments/lux-dao/494949.json
// Lux 96369 → lux/standard/deployments/org-safes/lux-96369.json (Safes)
// lux/standard/deployments/gov-vote/96369.json (dead Governor)
// Hanzo 36963 → pending (CTO agent deploying) — skip-gated below.
//
// These are the orgs' MAINNET chains (200200/494949/96369), pinned regardless of
// LUX_ENV — the DAO contracts live there, not on a per-run testnet.
// BountyV1 / ReputationV1 ABI selectors (keccak256(sig)[:4]).
const SEL = {
bountyCount: '0x3e362c96', // bountyCount() -> uint256
stateOf: '0x131a7e24', // stateOf(uint256) -> State
treasury: '0x61d027b3', // treasury() -> address
writer: '0x453a2abc', // ReputationV1.writer() -> address (the sole writer == BountyV1)
}
// IBountyV1.State enum: None,Open,Funded,Claimed,Submitted,Accepted,Paid(6),Disputed,Cancelled
const STATE_PAID = 6
function stateOfData(bountyId: number): string {
return SEL.stateOf + encodeUint(bountyId)
}
// ── Zoo DAO (200200) ───────────────────────────────────────────────
test.describe('DAO On-Chain — Zoo (200200)', () => {
const rpc = orgRpc('zoo', 'mainnet')
const BOUNTY = '0x857c9fE5A644e048FEF7194Da10fFEb356a81fFD'
const REPUTATION = '0xE6766AF6171F437D6d3952e73402b762718c1809'
const DAO_SAFE = '0x229599f227231d8C90fcF1a78589F5DC4b7A6962'
test('C-Chain is Zoo (chainId 200200)', async ({ request }) => {
const cid = await ethChainId(request, rpc)
console.log(` Zoo chainId: ${cid}`)
expect(cid).toBe(CHAIN_IDS.mainnet.zoo)
})
test('BountyV1 is deployed (has bytecode)', async ({ request }) => {
const code = await ethGetCode(request, rpc, BOUNTY)
console.log(` BountyV1 ${BOUNTY}: ${code.length} chars`)
expect(hasCode(code)).toBe(true)
})
test('BountyV1.bountyCount() >= 1', async ({ request }) => {
const res = await ethCall(request, rpc, BOUNTY, SEL.bountyCount)
const count = parseInt(res, 16)
console.log(` bountyCount: ${count}`)
expect(count).toBeGreaterThanOrEqual(1)
})
test('BountyV1.stateOf(0) == Paid (6)', async ({ request }) => {
const res = await ethCall(request, rpc, BOUNTY, stateOfData(0))
const state = parseInt(res, 16)
console.log(` stateOf(0): ${state} (expected ${STATE_PAID} Paid)`)
expect(state).toBe(STATE_PAID)
})
test('BountyV1.treasury() == Zoo DAO Safe', async ({ request }) => {
const res = await ethCall(request, rpc, BOUNTY, SEL.treasury)
const treasury = decodeAddress(res)
console.log(` treasury: ${treasury} (expected ${DAO_SAFE.toLowerCase()})`)
expect(treasury).toBe(DAO_SAFE.toLowerCase())
})
test('ReputationV1 is deployed and wired to BountyV1', async ({ request }) => {
const code = await ethGetCode(request, rpc, REPUTATION)
expect(hasCode(code)).toBe(true)
// The work-market is the SOLE writer of its reputation ledger — prove the wiring.
const writer = decodeAddress(await ethCall(request, rpc, REPUTATION, SEL.writer))
console.log(` ReputationV1 writer: ${writer} (expected ${BOUNTY.toLowerCase()})`)
expect(writer).toBe(BOUNTY.toLowerCase())
})
})
// ── Pars DAO (494949) ──────────────────────────────────────────────
test.describe('DAO On-Chain — Pars (494949)', () => {
const rpc = orgRpc('pars', 'mainnet')
const BOUNTY = '0x79254D4A9286FBd65E7177440Be20f00934c33c2'
const REPUTATION = '0xD7fe65BCb3Efd85AC1E132E8b74365498E034f13'
const GOVERNOR = '0x62Ea1B27CDD922dbAaE0572f4CD4862Ca939C24c'
const DAO_SAFE = '0x4CEA4ac1C874a340B06e0422E77a477463C3a542'
test('C-Chain is Pars (chainId 494949)', async ({ request }) => {
const cid = await ethChainId(request, rpc)
console.log(` Pars chainId: ${cid}`)
expect(cid).toBe(CHAIN_IDS.mainnet.pars)
})
test('BountyV1 is deployed (has bytecode)', async ({ request }) => {
const code = await ethGetCode(request, rpc, BOUNTY)
console.log(` BountyV1 ${BOUNTY}: ${code.length} chars`)
expect(hasCode(code)).toBe(true)
})
test('BountyV1.bountyCount() >= 1', async ({ request }) => {
const count = parseInt(await ethCall(request, rpc, BOUNTY, SEL.bountyCount), 16)
console.log(` bountyCount: ${count}`)
expect(count).toBeGreaterThanOrEqual(1)
})
test('BountyV1.stateOf(0) == Paid (6)', async ({ request }) => {
const state = parseInt(await ethCall(request, rpc, BOUNTY, stateOfData(0)), 16)
console.log(` stateOf(0): ${state} (expected ${STATE_PAID} Paid)`)
expect(state).toBe(STATE_PAID)
})
test('BountyV1.treasury() == Pars DAO Safe', async ({ request }) => {
const treasury = decodeAddress(await ethCall(request, rpc, BOUNTY, SEL.treasury))
console.log(` treasury: ${treasury} (expected ${DAO_SAFE.toLowerCase()})`)
expect(treasury).toBe(DAO_SAFE.toLowerCase())
})
test('ReputationV1 is deployed and wired to BountyV1', async ({ request }) => {
const code = await ethGetCode(request, rpc, REPUTATION)
expect(hasCode(code)).toBe(true)
const writer = decodeAddress(await ethCall(request, rpc, REPUTATION, SEL.writer))
console.log(` ReputationV1 writer: ${writer} (expected ${BOUNTY.toLowerCase()})`)
expect(writer).toBe(BOUNTY.toLowerCase())
})
test('Governor (Fractal module) is deployed', async ({ request }) => {
// Azorius/Fractal ModuleGovernor — no OZ name()/proposalCount() surface, so
// bytecode presence is the deploy proof (calling a view would revert).
const code = await ethGetCode(request, rpc, GOVERNOR)
console.log(` Governor ${GOVERNOR}: ${code.length} chars`)
expect(hasCode(code)).toBe(true)
})
test('DAO Safe is deployed', async ({ request }) => {
const code = await ethGetCode(request, rpc, DAO_SAFE)
console.log(` DAO Safe ${DAO_SAFE}: ${code.length} chars`)
expect(hasCode(code)).toBe(true)
})
})
// ── Hanzo DAO (36963) — skip-gated until the CTO agent's deploy lands ──
test.describe('DAO On-Chain — Hanzo (36963)', () => {
// Structure is IDENTICAL to Pars above. Flip these on the moment the deploy
// record lands at lux/dao/contracts/deployments/lux-dao/36963.json.
// TODO(hanzo-deploy): BOUNTY = 0x… (from 36963.json .contracts.bounty)
// TODO(hanzo-deploy): REPUTATION = 0x… (from 36963.json .contracts.reputation)
// TODO(hanzo-deploy): GOVERNOR = 0x… (from 36963.json .contracts.moduleGovernor)
// Known now (lux/standard org-safes): DAO_SAFE = 0x6402c198F59de46c6348a707271D7a600E82B0a7
test('BountyV1/Reputation/Governor/Safe deployed + wired', async () => {
test.skip(true, 'live after CTO agent deploy — flip when lux-dao/36963.json (bounty/reputation/governor) confirmed on 36963')
})
})
// ── Lux DAO (96369) — Governor live, no Safe on the chain ──────────────────
test.describe('DAO On-Chain — Lux (96369)', () => {
const rpc = orgRpc('lux', 'mainnet')
// The Governor that is actually deployed, taken from deployments/registry.json
// (generated from eth_getCode, unlike the hand-kept records) and confirmed by
// eth_call. It is not the address gov-vote/96369.json used to be read for, and
// it is not the one this file asserted was dead: that address is empty, and so
// is every Safe address any record on this chain names.
const GOVERNOR = '0x976520c30903F0744814D149574f9C0D9BaA1431'
const VOTES_TOKEN = '0x10192320Aea3444cf8e85EB617Ab47479Ea0e640'
// Three record sets name a Safe suite here and all of them are empty on chain:
// org-safes/lux-96369.json (singleton/factory/daoSafe/teamSafe/timelock), the
// addresses lux/dao carried in featuredDAOs, and the set this file used to
// assert had code. registry.json lists 29 live contracts on 96369 and no Safe.
const SAFES_THAT_DO_NOT_EXIST = {
'org-safes singleton': '0x4b87ea33b024bb5ba93cd87fb01fb530b835a85b',
'org-safes daoSafe': '0xDA215aab35CD29097B0d454042f676F1dA02497F',
'featuredDAOs (was)': '0xF0D19cCCB8e33663e9bDEe6e40474F1E3fD1f2BD',
'shipped in the bundle': '0xE54cAf7E0C04E0eC69BE9302B97613De89745DDC',
}
const SEL_CLOCK_MODE = '0x4bf5d7e9' // CLOCK_MODE() -> string
const SEL_VOTING_PERIOD = '0x02a251a3' // votingPeriod() -> uint256
const SEL_TOKEN = '0xfc0c546a' // token() -> address
test('C-Chain is Lux (chainId 96369)', async ({ request }) => {
const cid = await ethChainId(request, rpc)
console.log(` Lux chainId: ${cid}`)
expect(cid).toBe(CHAIN_IDS.mainnet.lux)
})
test('Governor is deployed', async ({ request }) => {
const code = await ethGetCode(request, rpc, GOVERNOR)
console.log(` Governor ${GOVERNOR}: ${code.length} chars`)
expect(hasCode(code)).toBe(true)
})
// The defect that made this governance unusable was not a missing deploy. The
// Governor took OpenZeppelin's default block-number clock, so votingDelay and
// votingPeriod — written as seconds — were read as block counts, which at this
// chain's rate turned 1 day into ~39 days and 7 days into ~270. A demand-driven
// chain stops the clock entirely when quiet. Reading the mode off-chain is the
// only way to know which unit the numbers below are in.
test('Governor counts in seconds, not blocks', async ({ request }) => {
const mode = await ethCall(request, rpc, GOVERNOR, SEL_CLOCK_MODE)
console.log(` CLOCK_MODE(): ${mode.includes('74696d657374616d70') ? 'mode=timestamp' : mode}`)
// "timestamp" as ABI-encoded string bytes.
expect(mode).toContain('74696d657374616d70')
const period = BigInt(await ethCall(request, rpc, GOVERNOR, SEL_VOTING_PERIOD))
console.log(` votingPeriod(): ${period}s = ${Number(period) / 86400}d`)
expect(period).toBe(604800n)
})
test('Governor votes the token that was deployed with it', async ({ request }) => {
const token = decodeAddress(await ethCall(request, rpc, GOVERNOR, SEL_TOKEN))
console.log(` token(): ${token}`)
expect(token).toBe(VOTES_TOKEN.toLowerCase())
})
// An assert, not a skip. lux.vote's treasury surface resolves a Safe by address
// before it will render, so while these are empty that surface cannot work on
// this chain and the app sends lux.vote to its governance route instead. The
// day a Safe is deployed this test fails — and that failure is the reminder to
// point lux/dao featuredDAOs at the real address rather than leaving the site
// on the governance-only path.
test('no Safe is deployed — every address any record names is empty', async ({ request }) => {
for (const [label, addr] of Object.entries(SAFES_THAT_DO_NOT_EXIST)) {
const code = await ethGetCode(request, rpc, addr)
console.log(` ${label} ${addr}: ${code === '0x' ? 'no code' : code.length + ' chars — A SAFE APPEARED'}`)
expect(hasCode(code), `${label} ${addr} now has code — wire lux.vote to it`).toBe(false)
}
})
})