@meddleware/dao-ui 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -0
- package/package.json +2 -1
- package/src/App.vue +2 -1
- package/src/components/AmountCell.vue +2 -0
- package/src/components/DataTable.vue +2 -0
- package/src/components/Panel.vue +1 -0
- package/src/components/ProposalRow.vue +2 -0
- package/src/components/StatusBar.vue +2 -0
- package/src/components/TabBar.vue +2 -0
- package/src/composables/useDaoEvents.ts +1 -3
- package/src/composables/useGates.ts +0 -2
- package/src/composables/usePlatformConfig.ts +3 -0
- package/src/composables/useTreasury.ts +2 -0
- package/src/config.ts +3 -0
- package/src/index.ts +2 -0
- package/src/main.ts +3 -0
- package/src/tabs/HistoryTab.vue +2 -2
- package/src/tabs/OverviewTab.vue +2 -1
- package/src/tabs/ProposalsTab.vue +2 -0
- package/src/tabs/TreasuryTab.vue +2 -7
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# @meddleware/dao-ui
|
|
2
|
+
|
|
3
|
+
The **Meddleware DAO console** — a standalone Vue 3 SPA (and embeddable library) that surfaces the
|
|
4
|
+
platform's on-chain state on Sui: treasury balance, commission policy, the community access gates the
|
|
5
|
+
treasury controls, and a live feed of access-gate activity.
|
|
6
|
+
|
|
7
|
+
Deployed at `sui-dao.meddleware.co.uk` and also embedded inline in the tools hub
|
|
8
|
+
(`sui.meddleware.co.uk`) as the default view.
|
|
9
|
+
|
|
10
|
+
## What it shows
|
|
11
|
+
|
|
12
|
+
The console is **read-only today** — no wallet connection is required to view any data. It reads
|
|
13
|
+
directly from Sui via a bare RPC client. Five tabs:
|
|
14
|
+
|
|
15
|
+
| Tab | Content |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| **Overview** | Treasury balance + commission rate, platform activity counters (active gates, accesses minted/consumed), and a recent-activity feed. |
|
|
18
|
+
| **Treasury** | Commission rate (bps), current SUI balance, treasury address, and the table of **Community Gates** the treasury controls (name, price, object id). |
|
|
19
|
+
| **Proposals** | Fundraising / governance proposals. **Placeholder** until the `vault_dao` on-chain module ships — shows an empty state today (see [Roadmap](#roadmap)). |
|
|
20
|
+
| **Governance** | Platform parameters (commission rate, the on-chain 10% / 1000-bps hard cap, treasury address) and an **Admin Actions** panel that detects whether the connected wallet holds a `PlatformAdminCap`. Controls light up only for cap holders (implementation pending). |
|
|
21
|
+
| **History** | Paginated on-chain event log (access sold / used / burned) with explorer links per address and transaction. |
|
|
22
|
+
|
|
23
|
+
## How gate discovery works
|
|
24
|
+
|
|
25
|
+
Gates are discovered by **`AdminCap` ownership**, not by events:
|
|
26
|
+
|
|
27
|
+
1. Read the treasury address from the `PlatformConfig` shared object.
|
|
28
|
+
2. List `access_gate::AdminCap` objects owned by the treasury.
|
|
29
|
+
3. Fetch each `Gate` shared object referenced by its `AdminCap.gate_id`.
|
|
30
|
+
|
|
31
|
+
This is deliberately **resilient to event pruning** — testnet fullnodes prune old events (including
|
|
32
|
+
the one-time `GateCreatedEvent`) after ~3 months, so event-based gate counting is unreliable. The
|
|
33
|
+
`AdminCap`→`Gate` path always reflects live on-chain reality. See
|
|
34
|
+
[`src/composables/useGates.ts`](src/composables/useGates.ts).
|
|
35
|
+
|
|
36
|
+
## Commission model
|
|
37
|
+
|
|
38
|
+
Commission is charged on every NFT access purchase and enforced on-chain by the `access_gate`
|
|
39
|
+
package: `commission_bps / 10000 × price` routes to the treasury; the remainder goes to the gate
|
|
40
|
+
operator. `commission_bps` is read live from `PlatformConfig`; the on-chain hard cap is 1000 bps
|
|
41
|
+
(10%).
|
|
42
|
+
|
|
43
|
+
## Configure (build-time `VITE_*`)
|
|
44
|
+
|
|
45
|
+
All values have working testnet defaults; override per network for mainnet.
|
|
46
|
+
|
|
47
|
+
| Var | Meaning |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `VITE_NETWORK` | `testnet` (default) or `mainnet` |
|
|
50
|
+
| `VITE_RPC_TESTNET` / `VITE_RPC_MAINNET` | Sui RPC URL override |
|
|
51
|
+
| `VITE_ACCESS_GATE_PACKAGE_ID_{NET}` | Published `access_gate` package id (governs which events/objects are queried) |
|
|
52
|
+
| `VITE_PLATFORM_CONFIG_ID_{NET}` | `PlatformConfig` shared-object id (commission + treasury) |
|
|
53
|
+
|
|
54
|
+
Mainnet package/config ids are empty until deployment; set them via the `_MAINNET` vars — no code
|
|
55
|
+
change required.
|
|
56
|
+
|
|
57
|
+
## Develop
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
npm install
|
|
61
|
+
npm run dev # vite dev server
|
|
62
|
+
npm run type-check # vue-tsc --noEmit
|
|
63
|
+
npm test # vitest
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Build
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
npm run build # vue-tsc + vite → dist/
|
|
70
|
+
docker build -t dao-ui .
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Use as a library
|
|
74
|
+
|
|
75
|
+
The package exports the core view so the dashboard (or any host) can render it inline against a
|
|
76
|
+
shared wallet:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { DaoView } from '@meddleware/dao-ui'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`DaoView` imports its own scoped stylesheet (`styles/qt.css`), so no consumer needs to import dao-ui
|
|
83
|
+
CSS separately. See [CLAUDE.md](CLAUDE.md) for the dual app+library architecture.
|
|
84
|
+
|
|
85
|
+
## Roadmap
|
|
86
|
+
|
|
87
|
+
- **`vault_dao` on-chain module** — proposals and governance are stubbed
|
|
88
|
+
([`src/composables/useProposals.ts`](src/composables/useProposals.ts) returns an empty list; the
|
|
89
|
+
Governance admin panel detects the cap but has no actions yet). When `vault_dao` ships, the
|
|
90
|
+
`Proposal` type and composable return shapes are kept stable so the UI wiring is unchanged.
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
BSD Zero Clause License (`0BSD`). See [LICENSE](LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meddleware/dao-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"homepage": "https://docs.meddleware.co.uk/blockchain/sui/dao/",
|
|
4
5
|
"description": "Meddleware DAO console — treasury overview, proposals, and governance on Sui.",
|
|
5
6
|
"author": "Meddleware <dev@meddleware.co.uk>",
|
|
6
7
|
"license": "0BSD",
|
package/src/App.vue
CHANGED
|
@@ -6,6 +6,7 @@ import { NETWORK } from './config.js'
|
|
|
6
6
|
import DaoView from './DaoView.vue'
|
|
7
7
|
|
|
8
8
|
const { mode, set } = useColorMode('dark')
|
|
9
|
+
const DOCS_URL = import.meta.env.VITE_DOCS_URL || 'https://docs.meddleware.co.uk/blockchain/sui/dao/'
|
|
9
10
|
</script>
|
|
10
11
|
|
|
11
12
|
<template>
|
|
@@ -22,7 +23,7 @@ const { mode, set } = useColorMode('dark')
|
|
|
22
23
|
|
|
23
24
|
<DaoView style="flex: 1; min-height: 0" />
|
|
24
25
|
|
|
25
|
-
<AppFooter />
|
|
26
|
+
<AppFooter :docs-url="DOCS_URL" />
|
|
26
27
|
</div>
|
|
27
28
|
</template>
|
|
28
29
|
|
package/src/components/Panel.vue
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
// Renders one fundraising/governance proposal: status badge, progress toward target,
|
|
3
|
+
// and epochs-left. The Contribute button is disabled until the vault_dao module ships.
|
|
2
4
|
import { computed } from 'vue'
|
|
3
5
|
import type { Proposal } from '../composables/useProposals.js'
|
|
4
6
|
|
|
@@ -3,7 +3,7 @@ import { getSuiClient } from '../wallet.js'
|
|
|
3
3
|
import { PACKAGE_ID } from '../config.js'
|
|
4
4
|
|
|
5
5
|
export interface DaoEvent {
|
|
6
|
-
type: 'AccessMinted' | 'AccessConsumed' | '
|
|
6
|
+
type: 'AccessMinted' | 'AccessConsumed' | 'AccessBurned'
|
|
7
7
|
txDigest: string
|
|
8
8
|
checkpoint: string | null
|
|
9
9
|
address?: string
|
|
@@ -24,7 +24,6 @@ export function useDaoEvents(limit = 20) {
|
|
|
24
24
|
const eventTypes = [
|
|
25
25
|
`${PACKAGE_ID}::access_gate::AccessMintedEvent`,
|
|
26
26
|
`${PACKAGE_ID}::access_gate::AccessConsumedEvent`,
|
|
27
|
-
`${PACKAGE_ID}::access_gate::GateCreatedEvent`,
|
|
28
27
|
]
|
|
29
28
|
|
|
30
29
|
const results = await Promise.allSettled(
|
|
@@ -41,7 +40,6 @@ export function useDaoEvents(limit = 20) {
|
|
|
41
40
|
const label =
|
|
42
41
|
typeName === 'AccessMintedEvent' ? 'AccessMinted'
|
|
43
42
|
: typeName === 'AccessConsumedEvent' ? 'AccessConsumed'
|
|
44
|
-
: typeName === 'GateCreatedEvent' ? 'GateCreated'
|
|
45
43
|
: 'AccessBurned'
|
|
46
44
|
const f = (e.json ?? {}) as Record<string, unknown>
|
|
47
45
|
all.push({
|
|
@@ -8,7 +8,6 @@ export interface Gate {
|
|
|
8
8
|
price: bigint
|
|
9
9
|
paused: boolean
|
|
10
10
|
frozen: boolean
|
|
11
|
-
checkpoint: string | null
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
export function useGates() {
|
|
@@ -57,7 +56,6 @@ export function useGates() {
|
|
|
57
56
|
price: BigInt(String(f.price_mist ?? '0')),
|
|
58
57
|
paused: Boolean(f.paused),
|
|
59
58
|
frozen: Boolean(f.frozen),
|
|
60
|
-
checkpoint: null,
|
|
61
59
|
}
|
|
62
60
|
} catch {
|
|
63
61
|
return null
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Reads the PlatformConfig shared object once on mount and exposes the treasury
|
|
2
|
+
// address + commission rate (bps). Source of truth for the commission model and for
|
|
3
|
+
// whose AdminCaps the gate discovery (useGates) looks up.
|
|
1
4
|
import { ref, onMounted } from 'vue'
|
|
2
5
|
import { getSuiClient } from '../wallet.js'
|
|
3
6
|
import { CONFIG_ID } from '../config.js'
|
package/src/config.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Build-time configuration. All chain ids are read from VITE_* env with working
|
|
2
|
+
// testnet defaults; mainnet ids stay empty until deployment (set the _MAINNET vars —
|
|
3
|
+
// no code change needed). PACKAGE_ID / CONFIG_ID / RPC_URL resolve to the active network.
|
|
1
4
|
export type Network = 'testnet' | 'mainnet'
|
|
2
5
|
|
|
3
6
|
export const NETWORK: Network = (import.meta.env.VITE_NETWORK as Network) || 'testnet'
|
package/src/index.ts
CHANGED
package/src/main.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Standalone SPA bootstrap. Loads the design-token + ui base stylesheets and the
|
|
2
|
+
// console's qt.css, then mounts the App shell. (The library path — src/index.ts —
|
|
3
|
+
// bypasses this and relies on DaoView's own scoped qt.css import instead.)
|
|
1
4
|
import '@meddleware/design-tokens/tokens.css'
|
|
2
5
|
import '@meddleware/ui/base.css'
|
|
3
6
|
import './styles/qt.css'
|
package/src/tabs/HistoryTab.vue
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
// History tab: paginated on-chain access-gate event log (sold / used / burned) with
|
|
3
|
+
// per-row explorer links. Data comes from useDaoEvents (pruning-tolerant, merged feed).
|
|
2
4
|
import { ref, computed } from 'vue'
|
|
3
5
|
import DataTable from '../components/DataTable.vue'
|
|
4
6
|
import { useDaoEvents } from '../composables/useDaoEvents.js'
|
|
@@ -22,7 +24,6 @@ function nextPage() { if (page.value < totalPages.value) page.value++ }
|
|
|
22
24
|
const eventLabel: Record<DaoEvent['type'], string> = {
|
|
23
25
|
AccessMinted: 'Access Sold',
|
|
24
26
|
AccessConsumed: 'Access Used',
|
|
25
|
-
GateCreated: 'Gate Created',
|
|
26
27
|
AccessBurned: 'Access Burned',
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -61,7 +62,6 @@ const eventLabel: Record<DaoEvent['type'], string> = {
|
|
|
61
62
|
class="dao-badge"
|
|
62
63
|
:class="{
|
|
63
64
|
'dao-badge--active': ev.type === 'AccessMinted',
|
|
64
|
-
'dao-badge--pending': ev.type === 'GateCreated',
|
|
65
65
|
'dao-badge--closed': ev.type === 'AccessConsumed' || ev.type === 'AccessBurned',
|
|
66
66
|
}"
|
|
67
67
|
>{{ eventLabel[ev.type] }}</span>
|
package/src/tabs/OverviewTab.vue
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
// Overview tab: treasury balance + commission, platform activity counters (active gates,
|
|
3
|
+
// accesses minted/consumed derived from the event feed), and a recent-activity list.
|
|
2
4
|
import { computed } from 'vue'
|
|
3
5
|
import Panel from '../components/Panel.vue'
|
|
4
6
|
import AmountCell from '../components/AmountCell.vue'
|
|
@@ -18,7 +20,6 @@ const { gates, loading: gatesLoading } = useGates()
|
|
|
18
20
|
const eventLabel: Record<DaoEvent['type'], string> = {
|
|
19
21
|
AccessMinted: 'Access sold',
|
|
20
22
|
AccessConsumed: 'Access used',
|
|
21
|
-
GateCreated: 'Gate created',
|
|
22
23
|
AccessBurned: 'Access burned',
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
// Proposals tab. Lists proposals via useProposals (currently a stub returning []),
|
|
3
|
+
// showing an empty state until the on-chain vault_dao module lands.
|
|
2
4
|
import ProposalRow from '../components/ProposalRow.vue'
|
|
3
5
|
import { useProposals } from '../composables/useProposals.js'
|
|
4
6
|
import { useEpoch } from '../composables/useEpoch.js'
|
package/src/tabs/TreasuryTab.vue
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
// Treasury tab: commission rate + live SUI balance + treasury address, plus the table
|
|
3
|
+
// of Community Gates the treasury controls (discovered via AdminCap ownership).
|
|
2
4
|
import { computed } from 'vue'
|
|
3
5
|
import Panel from '../components/Panel.vue'
|
|
4
6
|
import AmountCell from '../components/AmountCell.vue'
|
|
@@ -53,18 +55,11 @@ function formatPrice(mist: bigint): string {
|
|
|
53
55
|
<template #head>
|
|
54
56
|
<th>Gate name</th>
|
|
55
57
|
<th>Price</th>
|
|
56
|
-
<th>
|
|
57
|
-
Block
|
|
58
|
-
<abbr class="dao-info" title="Sui checkpoint sequence number at which this gate was created. Equivalent to a block height in other blockchains.">?</abbr>
|
|
59
|
-
</th>
|
|
60
58
|
<th>Object ID</th>
|
|
61
59
|
</template>
|
|
62
60
|
<tr v-for="gate in gates" :key="gate.id">
|
|
63
61
|
<td>{{ gate.name }}</td>
|
|
64
62
|
<td class="dao-amount">{{ formatPrice(gate.price) }}</td>
|
|
65
|
-
<td class="dao-mono" style="font-size: 0.72rem">
|
|
66
|
-
{{ gate.checkpoint ? '#' + gate.checkpoint : '—' }}
|
|
67
|
-
</td>
|
|
68
63
|
<td class="dao-mono" style="font-size: 0.7rem; white-space: nowrap">
|
|
69
64
|
<CopyableAddress :address="gate.id" label="Copy object ID">
|
|
70
65
|
<ExplorerLink :href="suiExplorerUrl('object', gate.id, NETWORK)" :value="gate.id" />
|