Regression-test the full DAO/governance/work-market surface across the live
orgs, read-only (no txs/writes/signing), asserting REAL functionality:
- 30-dao-onchain: BountyV1/ReputationV1/Governor/Safe on Zoo 200200 + Pars
494949 via eth_call/eth_getCode — code present, bountyCount>=1, stateOf(0)==
Paid(6), treasury()==org DAO Safe, ReputationV1.writer()==BountyV1 (wiring).
Lux 96369: canonical CREATE2 Safe suite present, dead Governor asserted 0-byte
(staged post-flag-day). Hanzo 36963 skip-gated until deploy lands.
- 31-vote-surfaces: lux/zoo/hanzo/pars.vote render + correct brand + ZERO cross-
brand/competitor leakage + no white-screen (#root descendants + body text) +
Connect Wallet present. reads-chain gated skip-until-live (Decent-fork shells).
- 32-work-boards: work.zoo.network LIVE badge (green, not FIXTURE), bounty in
Paid column, on-chain ReputationV1 block, UI bounty count == chain bountyCount.
work.{lux,hanzo,pars} skip-until-live.
- 33-bounty-bot: bounties.zoo.network webhook routed+POST-only (405), unsigned
POST rejected (401), healthz internal-scoped (404 public correct). No secrets.
- 34-pars-chain: Pars 494949 chainId + monotonic height + explorer renders.
helpers.ts: additive ethCall/ethGetCode/hasCode/decodeAddress/encodeUint +
HANZO/PARS API domains + orgRpc() (one way to resolve any org's C-Chain RPC).
17-governance: drop dead unused SELECTORS block (on-chain contract asserts now
own their home in 30-dao-onchain). package.json: wire specs into test:full +
per-spec scripts.
36 active tests pass live, 8 skip-gated (ready to flip). Suite stays GREEN.
Co-authored-by: Hanzo Dev <[email protected]>
160 lines
6.7 KiB
TypeScript
160 lines
6.7 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import {
|
|
getUrls,
|
|
rpcCall,
|
|
httpStatus, httpGetJson,
|
|
snap,
|
|
} from './helpers'
|
|
|
|
const urls = getUrls()
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Governance — DAO pages, proposal queries, staking-as-governance
|
|
// ---------------------------------------------------------------------------
|
|
// On-chain governance/DAO CONTRACT assertions (Bounty/Reputation/Governor/Safe
|
|
// via eth_call) live in 30-dao-onchain.spec.ts. This file covers the explorer
|
|
// discovery + staking-as-governance surface.
|
|
|
|
// ── Governance UI ──────────────────────────────────────────────────
|
|
test.describe('Governance — DAO Page', () => {
|
|
test('governance page loads if URL exists', async ({ request, page }) => {
|
|
const govPaths = [
|
|
`${urls.exchange}/governance`,
|
|
`${urls.exchange}/dao`,
|
|
`${urls.exchange}/vote`,
|
|
'https://dao.lux.network',
|
|
'https://vote.lux.network',
|
|
]
|
|
let found = false
|
|
for (const path of govPaths) {
|
|
const status = await httpStatus(request, path)
|
|
if (status === 200) {
|
|
const res = await page.goto(path, { timeout: 30_000, waitUntil: 'domcontentloaded' })
|
|
expect(res?.status()).toBe(200)
|
|
await page.waitForTimeout(3000)
|
|
const bodyLen = (await page.textContent('body'))?.length || 0
|
|
expect(bodyLen).toBeGreaterThan(0)
|
|
await snap(page, 'governance-dao')
|
|
found = true
|
|
console.log(` DAO page found: ${path}`)
|
|
break
|
|
}
|
|
}
|
|
if (!found) {
|
|
console.log(' No governance page found (governance UI may not be deployed yet)')
|
|
}
|
|
})
|
|
})
|
|
|
|
// ── Governance Contract Discovery ──────────────────────────────────
|
|
test.describe('Governance — Contract Discovery via Explorer', () => {
|
|
test('explorer search for governance contracts', async ({ request }) => {
|
|
const status = await httpStatus(
|
|
request,
|
|
`${urls.apiExploreLux}/search?q=governor`,
|
|
)
|
|
if (status === 200) {
|
|
const data = await httpGetJson(
|
|
request,
|
|
`${urls.apiExploreLux}/search?q=governor`,
|
|
) as { items?: { address: string; name: string; type: string }[] }
|
|
const count = data.items?.length ?? 0
|
|
console.log(` Governor contracts found: ${count}`)
|
|
if (data.items && data.items.length > 0) {
|
|
for (const item of data.items.slice(0, 3)) {
|
|
console.log(` ${item.name || 'unnamed'} (${item.type}) at ${item.address}`)
|
|
}
|
|
}
|
|
} else {
|
|
console.log(` Explorer search returned ${status}`)
|
|
}
|
|
// Explorer search endpoint should respond
|
|
expect([200, 404]).toContain(status)
|
|
})
|
|
|
|
test('explorer lists governance-related tokens', async ({ request }) => {
|
|
// Look for governance tokens (typically ERC-20 with "GOV" or "LUX" in name)
|
|
const data = await httpGetJson(
|
|
request,
|
|
`${urls.apiExploreLux}/tokens?q=governance&type=ERC-20`,
|
|
) as { items?: { address_hash?: string; address?: string; name: string; symbol: string }[] }
|
|
const count = data.items?.length ?? 0
|
|
console.log(` Governance tokens found: ${count}`)
|
|
if (data.items && data.items.length > 0) {
|
|
for (const token of data.items.slice(0, 5)) {
|
|
console.log(` ${token.name} (${token.symbol}) at ${token.address_hash || token.address}`)
|
|
}
|
|
}
|
|
expect(data).toBeDefined()
|
|
})
|
|
})
|
|
|
|
// ── On-Chain Governance Queries ────────────────────────────────────
|
|
test.describe('Governance — On-Chain State', () => {
|
|
test('can query ERC-20 token list for potential governance tokens', async ({ request }) => {
|
|
const data = await httpGetJson(
|
|
request,
|
|
`${urls.apiExploreLux}/tokens?type=ERC-20`,
|
|
) as { items?: { address_hash?: string; address?: string; name: string; symbol: string; total_supply: string }[] }
|
|
const count = data.items?.length ?? 0
|
|
console.log(` ERC-20 tokens indexed: ${count}`)
|
|
if (data.items && data.items.length > 0) {
|
|
for (const token of data.items.slice(0, 5)) {
|
|
console.log(` ${token.name} (${token.symbol}) supply=${token.total_supply} at ${token.address_hash || token.address}`)
|
|
}
|
|
}
|
|
expect(data).toBeDefined()
|
|
})
|
|
|
|
test('verified contracts list accessible', async ({ request }) => {
|
|
const data = await httpGetJson(
|
|
request,
|
|
`${urls.apiExploreLux}/smart-contracts`,
|
|
) as { items?: { address: { hash: string }; name: string; verified_at: string }[] }
|
|
const count = data.items?.length ?? 0
|
|
console.log(` Verified contracts: ${count}`)
|
|
if (data.items && data.items.length > 0) {
|
|
for (const contract of data.items.slice(0, 5)) {
|
|
console.log(` ${contract.name} at ${contract.address?.hash}`)
|
|
}
|
|
}
|
|
expect(data).toBeDefined()
|
|
})
|
|
})
|
|
|
|
// ── Staking as Governance ──────────────────────────────────────────
|
|
test.describe('Governance — Staking as Governance Mechanism', () => {
|
|
test('validator delegation fees represent governance parameters', async ({ request }) => {
|
|
// In Lux, staking IS governance — validator weights determine consensus power
|
|
const result = await rpcCall(
|
|
request, urls.rpcPlatform, 'platform.getCurrentValidators', { subnetID: '' },
|
|
) as { validators: { nodeID: string; weight: string; delegationFee: string }[] }
|
|
const validators = result.validators
|
|
expect(validators.length).toBeGreaterThan(0)
|
|
|
|
// Calculate total weight (voting power)
|
|
const totalWeight = validators.reduce((sum, v) => sum + BigInt(v.weight), 0n)
|
|
console.log(` Total network weight (governance power): ${totalWeight}`)
|
|
expect(totalWeight).toBeGreaterThan(0n)
|
|
|
|
// Check weight distribution (no single validator should have >33% weight)
|
|
for (const v of validators) {
|
|
const pct = (Number(BigInt(v.weight) * 10000n / totalWeight) / 100)
|
|
console.log(` ${v.nodeID}: ${pct.toFixed(2)}% weight`)
|
|
// Warning threshold: >33% is concerning for governance
|
|
if (pct > 33) {
|
|
console.log(` WARNING: ${v.nodeID} has ${pct.toFixed(2)}% of total weight`)
|
|
}
|
|
}
|
|
})
|
|
|
|
test('network has sufficient decentralization (>= 5 validators)', async ({ request }) => {
|
|
const result = await rpcCall(
|
|
request, urls.rpcPlatform, 'platform.getCurrentValidators', { subnetID: '' },
|
|
) as { validators: { nodeID: string }[] }
|
|
const count = result.validators.length
|
|
console.log(` Validator count (decentralization metric): ${count}`)
|
|
expect(count).toBeGreaterThanOrEqual(5)
|
|
})
|
|
})
|