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]>
This commit is contained in:
+60
-28
@@ -161,17 +161,32 @@ test.describe('DAO On-Chain — Hanzo (36963)', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// ── Lux DAO (96369) — Safes/masters live, Governor dead post-flag-day ──
|
||||
// ── Lux DAO (96369) — Governor live, no Safe on the chain ──────────────────
|
||||
test.describe('DAO On-Chain — Lux (96369)', () => {
|
||||
const rpc = orgRpc('lux', 'mainnet')
|
||||
// Canonical CREATE2-deterministic Safe suite (org-safes/lux-96369.json) — the
|
||||
// suite that SUPERSEDED the nonce-CREATE addresses lost on the 2026-07 RLP re-import.
|
||||
const SAFE_SINGLETON = '0xED96250C6cDca3B5e5F8F066Fc3e200Ebe08BA87'
|
||||
const DAO_SAFE = '0xE54cAf7E0C04E0eC69BE9302B97613De89745DDC' // Vault / daoSafe
|
||||
const DAO_GOV_SAFE = '0x8E29b816c6C35b13cE1ff68D33E245C2bda8ac3D' // daoGovernanceSafe
|
||||
// Historical Governor (gov-vote/96369.json, superseded=true) — never deployed on
|
||||
// mainnet; recorded here ONLY to assert it stays dead until post-flag-day rollout.
|
||||
const DEAD_GOVERNOR = '0x12f0a4aa7d7c980915382739c7bdfd14a6105ad9'
|
||||
|
||||
// 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)
|
||||
@@ -179,29 +194,46 @@ test.describe('DAO On-Chain — Lux (96369)', () => {
|
||||
expect(cid).toBe(CHAIN_IDS.mainnet.lux)
|
||||
})
|
||||
|
||||
test('Safe singleton (master) is deployed', async ({ request }) => {
|
||||
const code = await ethGetCode(request, rpc, SAFE_SINGLETON)
|
||||
console.log(` Safe singleton ${SAFE_SINGLETON}: ${code.length} chars`)
|
||||
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)
|
||||
})
|
||||
|
||||
test('DAO Safe + DAO Governance Safe are deployed', async ({ request }) => {
|
||||
const daoCode = await ethGetCode(request, rpc, DAO_SAFE)
|
||||
const govCode = await ethGetCode(request, rpc, DAO_GOV_SAFE)
|
||||
console.log(` DAO Safe ${DAO_SAFE}: ${daoCode.length} chars`)
|
||||
console.log(` DAO Gov Safe ${DAO_GOV_SAFE}: ${govCode.length} chars`)
|
||||
expect(hasCode(daoCode)).toBe(true)
|
||||
expect(hasCode(govCode)).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 is DEAD (0 bytes) — governance stack staged post-flag-day', async ({ request }) => {
|
||||
// Mainnet 96369 is frozen at the flag-day export tip; the governance +
|
||||
// work-market stack deploys AFTER flag-day via DeployDAO.s.sol (proven on
|
||||
// testnet 96368 first). Until then the Governor address must carry NO code.
|
||||
// This is an assert, not a skip: a non-zero result here means a premature /
|
||||
// unaudited governance deploy landed on the treasury chain — a real regression.
|
||||
const code = await ethGetCode(request, rpc, DEAD_GOVERNOR)
|
||||
console.log(` Governor ${DEAD_GOVERNOR}: ${code === '0x' ? 'no code (dead, expected)' : code.length + ' chars — UNEXPECTED'}`)
|
||||
expect(hasCode(code)).toBe(false)
|
||||
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)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user