@meddleware/token-deployer-ui 0.0.10 → 0.0.11
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 +8 -5
- package/package.json +19 -8
- package/src/App.vue +1 -1
- package/src/component-styles.css +39 -57
- package/src/components/GithubPush.vue +2 -2
- package/src/components/IconPicker.vue +44 -75
- package/src/components/LicensePicker.vue +2 -2
- package/src/components/ResultPanel.vue +2 -2
- package/src/components/ReviewPanel.vue +11 -3
- package/src/components/TokenDeployerView.vue +52 -48
- package/src/components/WalletBar.vue +12 -1
- package/src/components/WalrusBlobBrowser.vue +4 -4
- package/src/composables/useWalrusRelay.ts +1 -2
- package/src/config.ts +19 -17
- package/src/lib/buildPublishTx.ts +0 -10
- package/src/lib/deploy.ts +9 -3
- package/src/lib/deployExecutor.ts +18 -45
- package/src/lib/template.ts +3 -0
- package/src/lib/walrus-constants.ts +2 -2
- package/src/main.ts +4 -6
- package/src/styles.css +22 -0
- package/.claude/move-language-reference/SKILL.md +0 -445
- package/.claude/skills/move-language-reference/SKILL.md +0 -445
- package/.claude/skills/sui-move-bootcamp/SKILL.md +0 -128
- package/.claude/sui-move-bootcamp/SKILL.md +0 -128
- package/.editorconfig +0 -8
- package/.env.example +0 -72
- package/.gitattributes +0 -1
- package/.github/workflows/docker-publish.yml +0 -348
- package/.github/workflows/e2e-realchain.yml +0 -64
- package/.github/workflows/node-ci.yml +0 -43
- package/.github/workflows/npm-publish.yml +0 -83
- package/.htmlvalidate.mjs +0 -30
- package/.oxfmtrc.json +0 -5
- package/.oxlintrc.json +0 -10
- package/.vscode/extensions.json +0 -9
- package/AGENTS.md +0 -39
- package/CLAUDE.md +0 -166
- package/Dockerfile +0 -96
- package/eslint.config.ts +0 -63
- package/index.html +0 -22
- package/playwright.config.ts +0 -30
- package/postcss.config.js +0 -8
- package/public/CNAME +0 -1
- package/public/_headers +0 -12
- package/public/_redirects +0 -1
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +0 -5
- package/public/og-image.png +0 -0
- package/public/robots.txt +0 -3
- package/public/sitemap.xml +0 -8
- package/scripts/e2e-browser.mjs +0 -15
- package/scripts/e2e-deploy.mjs +0 -227
- package/scripts/e2e-localnet.mjs +0 -67
- package/scripts/e2e-walrus-browser.mjs +0 -112
- package/scripts/gen-og-image.mjs +0 -31
- package/scripts/publish-localnet.mjs +0 -56
- package/scripts/regen-template.sh +0 -45
- package/scripts/sync-template-src.sh +0 -36
- package/scripts/verify-bytecode.mjs +0 -49
- package/src/__tests__/App.spec.js +0 -11
- package/src/composables/useAccessGate.ts +0 -127
- package/src/lib/accessGate.ts +0 -155
- package/src/lib/walrus.ts +0 -155
- package/stylelint.config.js +0 -33
- package/test-results/.last-run.json +0 -4
- package/tests/accessGate.test.ts +0 -113
- package/tests/buildPublishTx.test.ts +0 -243
- package/tests/deploy.test.ts +0 -149
- package/tests/errors.test.ts +0 -32
- package/tests/generatePackage.test.ts +0 -193
- package/tests/github.test.ts +0 -61
- package/tests/licenses.test.ts +0 -81
- package/tests/listMyTokens.test.ts +0 -30
- package/tests/template.test.ts +0 -93
- package/tests/templateArtifact.test.ts +0 -61
- package/tests/templateParity.test.ts +0 -55
- package/tests/useAccessGate.test.ts +0 -97
- package/tests/useWallet.test.ts +0 -74
- package/tests/useWalrusRelayGating.test.ts +0 -30
- package/tests/validation.test.ts +0 -117
- package/tests/walrusUpload.test.ts +0 -93
- package/tests/walrus_url_patch.test.ts +0 -26
- package/tsconfig.json +0 -19
- package/typedoc.json +0 -34
- package/vite.config.ts +0 -73
- package/vitest.config.ts +0 -10
package/AGENTS.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# AGENTS.md — token-deployer-ui
|
|
2
|
-
|
|
3
|
-
Browser app for permissionless Sui token deployment. See [CLAUDE.md](CLAUDE.md) for full architecture and invariant documentation.
|
|
4
|
-
|
|
5
|
-
## Responsibilities
|
|
6
|
-
|
|
7
|
-
- Bytecode patching of the pre-compiled Move template (`src/lib/template.ts`)
|
|
8
|
-
- PTB construction for two-phase publish + finalize (`src/lib/buildPublishTx.ts`)
|
|
9
|
-
- Source package generation and zip download (`src/lib/generatePackage.ts`)
|
|
10
|
-
- Walrus icon upload via the operator relay (`@meddleware/sui-walrus`)
|
|
11
|
-
- NFT-gate ownership check and access-proof attachment when `VITE_ACCESS_GATE_*` is configured
|
|
12
|
-
|
|
13
|
-
## Non-responsibilities
|
|
14
|
-
|
|
15
|
-
- On-chain accounting or financial enforcement — the contracts handle that
|
|
16
|
-
- Any backend process; no server dependency
|
|
17
|
-
- Custody of keys or user funds
|
|
18
|
-
|
|
19
|
-
## Testing
|
|
20
|
-
|
|
21
|
-
Three distinct categories — do not collapse:
|
|
22
|
-
|
|
23
|
-
1. **Unit tests** (`npm test`, Vitest): validation, template patching, PTB builders, generatePackage, deploy orchestration.
|
|
24
|
-
2. **Mocked-RPC e2e** (`npm run test:e2e`, Playwright, Chromium + Firefox): UI flow with fetch mock + mock wallet. Specs live in `integrations/token-deployer/e2e/`.
|
|
25
|
-
3. **Real-chain e2e** (`npm run e2e:localnet|testnet|mainnet`): publishes a real token; **never run in automatic CI**; gated by `E2E_MAINNET_CONFIRM=1` on mainnet.
|
|
26
|
-
|
|
27
|
-
## Key invariants
|
|
28
|
-
|
|
29
|
-
- **Template parity:** `.mv` bytes and `files.json` must derive from the same `sui-token-template` commit. Verify with `npm run verify:template`; regenerate with `npm run regen:template && npm run sync:template`.
|
|
30
|
-
- **Fee integrity:** treasury fee is split from gas inside the publish PTB. The vite prod build fails if `VITE_FEE_TREASURY` is unset (`assertTreasuryConfigured`).
|
|
31
|
-
- **Injection safety:** validation in `validation.ts` + independent re-assertion in `generatePackage.ts` and `template.ts`. Never remove a downstream guard.
|
|
32
|
-
- **No wallet-standard proxying:** Sui wallet objects must be wrapped in `markRaw()` before storing in Vue reactive state (private-field getters crash under Vue's Proxy).
|
|
33
|
-
|
|
34
|
-
## Key paths
|
|
35
|
-
|
|
36
|
-
- E2e specs: `e2e/` (mocked-RPC Playwright suite; `testDir` in `playwright.config.ts`)
|
|
37
|
-
- Move template: `@meddleware/sui-token-template` (`optionalDependencies` in `package.json`; local checkout at `../sui-token-template/` if working from the workspace)
|
|
38
|
-
- NFT gate client: `@meddleware/nft-gate-client` (`repos/nft-gate-client/` in the workspace)
|
|
39
|
-
- CI workflow: `.github/workflows/token-deployer-e2e.yml`
|
package/CLAUDE.md
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md — `token-deployer-ui`
|
|
2
|
-
|
|
3
|
-
`@meddleware/token-deployer-ui` is a Vue 3 + Vite browser app that lets any user deploy
|
|
4
|
-
their own Sui coin fully client-side (their wallet signs + pays gas + a trivial fee to
|
|
5
|
-
the operator treasury) and download a matching source package. It is the browser
|
|
6
|
-
equivalent of the `@meddleware/sui-token-template` Move package (from which it derives
|
|
7
|
-
its pre-compiled bytecode and source templates). See the repo-root
|
|
8
|
-
[CLAUDE.md](../../../../CLAUDE.md) for context and [README.md](README.md) for the
|
|
9
|
-
user/operator guide.
|
|
10
|
-
|
|
11
|
-
## Dual app + library pattern
|
|
12
|
-
|
|
13
|
-
This package ships as both a **standalone SPA** and an **embeddable library component**:
|
|
14
|
-
|
|
15
|
-
- **Standalone SPA** (`npm run dev` / `npm run build`): `src/main.ts` mounts `App.vue`, which
|
|
16
|
-
is a thin shell that renders `WalletBar` + `TokenDeployerView`. All deploy logic lives in
|
|
17
|
-
`TokenDeployerView` — `App.vue` contains no state.
|
|
18
|
-
- **Library component** (consumed by `@meddleware/dashboard`): `src/index.ts` exports
|
|
19
|
-
`TokenDeployerView`, which is imported via the `exports["."]` entry in `package.json`. The
|
|
20
|
-
dashboard's Vite build resolves TypeScript source directly — no separate lib build step.
|
|
21
|
-
The dashboard supplies the wallet connection via `@meddleware/wallet-adapter`'s shared
|
|
22
|
-
module singleton; `TokenDeployerView` reads `useWallet()` / `useNetwork()` from it and is
|
|
23
|
-
wrapped in `WalletGuard` so it shows a connect prompt when no wallet is connected.
|
|
24
|
-
|
|
25
|
-
The pattern mirrors `@meddleware/walrus-ui`, `@meddleware/seal-ui`, and
|
|
26
|
-
`@meddleware/access-gate-ui`. See the dashboard `CLAUDE.md` for the invariant: every tool view
|
|
27
|
-
must **not** render its own wallet bar, header, or footer when embedded.
|
|
28
|
-
|
|
29
|
-
**Deploy executor note:** The wallet-adapter `Executor` only returns `{ digest }` — insufficient
|
|
30
|
-
for the deploy flow, which requires the created objects to extract the packageId, coin type,
|
|
31
|
-
TreasuryCap, MetadataCap and the Currency ref. `src/lib/deployExecutor.ts` uses the wallet-adapter
|
|
32
|
-
singleton for wallet connection state, but executes through the app's own `SuiGrpcClient`
|
|
33
|
-
(`getReadClient`) with `include: { effects, objectTypes }`, and `toSuiTxResult` (`src/lib/deploy.ts`)
|
|
34
|
-
maps `effects.changedObjects` onto the `objectChanges` shape `extractPublishResult` reads. This is
|
|
35
|
-
intentional — do not replace it with the wallet-adapter `buildExecutor`.
|
|
36
|
-
|
|
37
|
-
**gRPC only.** Public fullnodes have deprecated JSON-RPC; every chain call (app and `scripts/`)
|
|
38
|
-
goes through `SuiGrpcClient`, and `VITE_RPC_*` / `E2E_RPC` must be gRPC(-web) endpoints. Do not
|
|
39
|
-
reintroduce `@mysten/sui/jsonRpc`. gRPC may render framework addresses in long form
|
|
40
|
-
(`0x000…02::coin::…`) — match types tolerantly. E2E builds (`VITE_E2E=1`) inject a stub client via
|
|
41
|
-
`setReadClient` because gRPC-web responses cannot be faked at the `fetch` layer.
|
|
42
|
-
|
|
43
|
-
## Architecture (the money/parity paths matter most)
|
|
44
|
-
|
|
45
|
-
- **Bytecode patching** — [src/lib/template.ts](src/lib/template.ts) deserialises
|
|
46
|
-
one pre-compiled template module ([src/move-template/](src/move-template/)),
|
|
47
|
-
renames the module/OTW-struct identifiers in place, and overwrites five DISTINCT
|
|
48
|
-
named constants (decimals, symbol, name, description, iconUrl), then re-serialises.
|
|
49
|
-
Binary format v7 cannot be round-tripped by `update_identifiers`/`update_constants`
|
|
50
|
-
("missing field version"); `deserialize`→mutate JSON→`serialize` is the path.
|
|
51
|
-
- **Two-phase publish** — TreasuryCap/MetadataCap are created in `init()` and sent
|
|
52
|
-
to the sender, so they are NOT `tx.publish` results. Publish PTB
|
|
53
|
-
([src/lib/buildPublishTx.ts](src/lib/buildPublishTx.ts)) = publish + UpgradeCap
|
|
54
|
-
policy + fee split from gas. A finalize PTB (only when needed —
|
|
55
|
-
`needsFinalize`) mints supply and applies supply/metadata policies.
|
|
56
|
-
- **Orchestration** — [src/lib/deploy.ts](src/lib/deploy.ts) sequences patch →
|
|
57
|
-
publish → wait → (finalize) via an injectable `Executor` (so it is unit-testable;
|
|
58
|
-
the real one is built from the wallet in
|
|
59
|
-
[src/lib/deployExecutor.ts](src/lib/deployExecutor.ts)).
|
|
60
|
-
- **Package generation** — [src/lib/generatePackage.ts](src/lib/generatePackage.ts)
|
|
61
|
-
substitutes the same inputs into the canonical template text
|
|
62
|
-
([src/template-src/files.json](src/template-src/files.json)) and zips it.
|
|
63
|
-
|
|
64
|
-
## Invariants (do not break)
|
|
65
|
-
|
|
66
|
-
1. **Bytecode ↔ source parity.** The shipped `.mv` and `files.json` must both derive
|
|
67
|
-
from the same `sui-token-template` commit. Enforced by
|
|
68
|
-
[tests/templateArtifact.test.ts](tests/templateArtifact.test.ts) (defaults present
|
|
69
|
-
in the `.mv`) and [tests/templateParity.test.ts](tests/templateParity.test.ts)
|
|
70
|
-
(`files.json` == canonical sources). After changing the template run
|
|
71
|
-
`npm run regen:template && npm run sync:template && npm run verify:template`.
|
|
72
|
-
2. **Injection safety is layered.** `validation.ts` gates the form, but
|
|
73
|
-
`generatePackage.ts` (`assertSafeConfig`) and `template.ts` (`patchVecConstant`)
|
|
74
|
-
re-assert `SAFE_TEXT`/`MOVE_IDENT`/length/keyword rules independently. Never
|
|
75
|
-
remove a downstream guard on the assumption the form already checked.
|
|
76
|
-
3. **Fee integrity.** The fee is split from `tx.gas` to the config treasury inside
|
|
77
|
-
the publish PTB. The vite production build fails on a zero-address treasury
|
|
78
|
-
(`assertTreasuryConfigured` in [vite.config.ts](vite.config.ts)).
|
|
79
|
-
4. **No backend / no custody / no endorsement.** Keep all financial truth on-chain;
|
|
80
|
-
the app only previews and constructs transactions. Do not add a server dependency.
|
|
81
|
-
5. **Lazy heavy deps.** `@mysten/walrus` and `@mysten/move-bytecode-template` (wasm)
|
|
82
|
-
are loaded on demand (dynamic import / `?url`). Keep the shared `ICON_EPOCHS`
|
|
83
|
-
constant in [src/lib/walrus-constants.ts](src/lib/walrus-constants.ts) so the
|
|
84
|
-
eagerly-rendered widget doesn't pull the Walrus chunk into the main bundle.
|
|
85
|
-
|
|
86
|
-
## Walrus icons
|
|
87
|
-
|
|
88
|
-
Icons are RAW blobs (not quilts) so `GET /v1/blobs/<id>` renders the exact image;
|
|
89
|
-
an upload relay is required from browsers. `ICON_EPOCHS = 53` (Walrus's
|
|
90
|
-
`max_epochs_ahead`; a larger single reservation aborts). Longer retention needs
|
|
91
|
-
`extendBlobLifetime` (no keeper — operator step). Verified on testnet via
|
|
92
|
-
`scripts/e2e-walrus-browser.mjs`.
|
|
93
|
-
|
|
94
|
-
## Walrus relay economics & deferred decisions
|
|
95
|
-
|
|
96
|
-
The icon upload can charge users per upload via Walrus's **native relay tip** —
|
|
97
|
-
no custom backend. This section records the decisions and their rationale so they
|
|
98
|
-
survive independently of any planning doc. (Verified against walrus SDK 1.1.7 and
|
|
99
|
-
the relay source `crates/walrus-upload-relay`.)
|
|
100
|
-
|
|
101
|
-
- **Native `linear` tip is enforced on-chain — chosen for launch.** `register`
|
|
102
|
-
bakes an auth payload `blobDigest ‖ SHA256(nonce) ‖ size` **plus** a tip transfer
|
|
103
|
-
to the operator's address into the *same* tx the user signs. The relay verifies
|
|
104
|
-
that on-chain tip (amount checked against the *actual received body length*, bound
|
|
105
|
-
by nonce/size) **before** the expensive storage-node fan-out. Stripping or lowering
|
|
106
|
-
the tip → the relay rejects the upload; no operator cost. This refutes an earlier
|
|
107
|
-
finding that the tip "could be removed" — it is enforced now.
|
|
108
|
-
- **The relay does no on-chain tx** (README L27) → the operator never pays gas.
|
|
109
|
-
Storage (WAL) is paid by the **user** at registration; read/serve bandwidth is the
|
|
110
|
-
separate public **aggregator**. The tip only needs to cover the relay's
|
|
111
|
-
ingress + fan-out egress + RPC lookups.
|
|
112
|
-
- **NFT / usage-ticket per-wallet gating is IMPLEMENTED (2026-08-05)** via the standalone
|
|
113
|
-
`nft-gate` project: an `nft-gate` gateway fronts the operator relay and admits only
|
|
114
|
-
holders of an `access_gate` NFT. The app gates the operator-relay option on ownership
|
|
115
|
-
(`useAccessGate` → `listOwnedObjects`), offers a permissionless purchase CTA, and attaches
|
|
116
|
-
a wallet-signed access proof (`sui:signPersonalMessage` → `uploadRelayAuthToken`) when the
|
|
117
|
-
operator relay is used. Unset `VITE_ACCESS_GATE_*` → no gating (unchanged behaviour). The
|
|
118
|
-
tip and the NFT are **independent levers** (tip = per-upload cost; NFT = access/abuse).
|
|
119
|
-
- **Per-wallet rate limiting** — previously rejected at the edge (which can't resolve the
|
|
120
|
-
wallet), it is now enforced by the `nft-gate` gateway *after* verification. Keep a per-IP
|
|
121
|
-
edge limit as a coarse pre-auth control.
|
|
122
|
-
- **Autonomous price-fed tip tuner DEFERRED** — the relay reads its tip only at
|
|
123
|
-
**startup** (no hot-reload/SIGHUP), so adjusting = rewrite YAML + restart. That's a
|
|
124
|
-
keeper with a liveness dependency. For launch use a **single static tip with a fat
|
|
125
|
-
SUI-denominated margin**: icons are cents-scale, so a large margin costs users
|
|
126
|
-
nothing and absorbs *both* SUI/fiat drift and storage-node retry amplification in
|
|
127
|
-
one lever. A later tuner (read Pyth on-chain, clamp to `[min,max]`, rolling-restart)
|
|
128
|
-
must stay under the client `max` headroom below.
|
|
129
|
-
- **Client `WALRUS_MAX_TIP_MIST` ↔ relay-tip COUPLING** — the SDK **throws** if the
|
|
130
|
-
relay's tip exceeds this client ceiling ([config.ts](src/config.ts) documents it).
|
|
131
|
-
Set it with generous headroom above the relay tip; raising the relay tip past it
|
|
132
|
-
needs a frontend redeploy.
|
|
133
|
-
- **Client-side icon size/type limits are UX only — NOT security.** The authoritative
|
|
134
|
-
cap is a hard request-body limit at the edge (the relay otherwise buffers up to a
|
|
135
|
-
hardcoded 1 GiB body into RAM *before* the tip check — the one real abuse vector:
|
|
136
|
-
ingress/RAM/RPC on rejected uploads). See the relay runbook in [README.md](README.md).
|
|
137
|
-
|
|
138
|
-
## Testing
|
|
139
|
-
|
|
140
|
-
Three distinct categories — do not collapse them (repo convention):
|
|
141
|
-
|
|
142
|
-
- **Pure unit (Vitest, `npm test`):** validation, licenses, github, template patch, PTB
|
|
143
|
-
builders, generatePackage, deploy orchestration, provenance/parity.
|
|
144
|
-
- **Mocked-RPC UI e2e (Playwright, `npm run test:e2e`):** wallet/form/publish flow against the
|
|
145
|
-
`VITE_E2E` fetch mock + mock wallet in [src/main.ts](src/main.ts) (tree-shaken from prod). Specs
|
|
146
|
-
live in `e2e/` (`playwright.config.ts` builds with `VITE_E2E=1` + previews). **Migrated from Cypress
|
|
147
|
-
2026-08-07** (Cypress dropped — it exits SIGILL in the sandbox; Playwright is the one browser-e2e
|
|
148
|
-
tool now, shared with the real-chain harness). Run `npm run e2e:install` once, then `npm run test:e2e`.
|
|
149
|
-
Cannot reach real confirmation, the result panel, or the source download — those need a real chain
|
|
150
|
-
(see below). This is the automatic gate alongside `npm test`.
|
|
151
|
-
- **Real-chain deploy e2e (Playwright, NOT in `npm test`):** `scripts/e2e-deploy.mjs` — the
|
|
152
|
-
single network-parametrized harness (`E2E_NETWORK=localnet|testnet|mainnet`) driven by
|
|
153
|
-
`npm run e2e:localnet|e2e:testnet|e2e:mainnet`. Injects a wallet-standard wallet backed by a
|
|
154
|
-
node keypair, does a REAL publish, waits for real confirmation, checks the result panel,
|
|
155
|
-
downloads the zip, and verifies coin type + fee on-chain. Two axes are distinct: `E2E_NETWORK`
|
|
156
|
-
(physical chain) vs the app network (testnet/mainnet UI mode; localnet borrows testnet mode with
|
|
157
|
-
the RPC pointed at the local node). Mainnet is fail-safe: aborts unless `E2E_MAINNET_CONFIRM=1`.
|
|
158
|
-
`scripts/e2e-browser.mjs` is a thin `E2E_NETWORK=localnet` wrapper; `scripts/e2e-walrus-browser.mjs`
|
|
159
|
-
(testnet icon upload) uses the E2E-only `window.__getSuiClient` hook in `src/main.ts`. Testnet/
|
|
160
|
-
mainnet are **manual only** — never in automatic CI (see the README launch runbook). The
|
|
161
|
-
`.github/workflows/token-deployer-e2e.yml` `workflow_dispatch` job is the gated manual runner.
|
|
162
|
-
|
|
163
|
-
## Docs
|
|
164
|
-
|
|
165
|
-
TSDoc on every exported symbol in `src/lib`, `src/composables`, and `src/config.ts`;
|
|
166
|
-
`npm run docs` renders a TypeDoc site to `docs/api/` (config: [typedoc.json](typedoc.json)).
|
package/Dockerfile
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
# ── build stage ───────────────────────────────────────────────────────────────
|
|
2
|
-
# Standalone build — the Docker context is this repo root. All @meddleware/*
|
|
3
|
-
# dependencies resolve from the npm registry, so they must be published before
|
|
4
|
-
# this image is built.
|
|
5
|
-
#
|
|
6
|
-
# docker build \
|
|
7
|
-
# --build-arg VITE_NETWORK=testnet \
|
|
8
|
-
# --build-arg VITE_FEE_TREASURY_TESTNET=0x... \
|
|
9
|
-
# --build-arg VITE_FEE_TREASURY_MAINNET=0x... \
|
|
10
|
-
# -t token-deployer-ui:<tag> .
|
|
11
|
-
#
|
|
12
|
-
# Build args (VITE_* are baked into the static bundle at build time):
|
|
13
|
-
# VITE_NETWORK — "testnet" | "mainnet" (default: testnet)
|
|
14
|
-
# VITE_FEE_TREASURY_TESTNET — operator treasury for testnet (REQUIRED — build fails without it)
|
|
15
|
-
# VITE_FEE_TREASURY_MAINNET — operator treasury for mainnet (REQUIRED — build fails without it)
|
|
16
|
-
# VITE_FEE_MIST — fee in MIST (optional, default 1000000000)
|
|
17
|
-
# VITE_WALRUS_RELAY_TESTNET — operator relay URL for testnet (optional)
|
|
18
|
-
# VITE_WALRUS_RELAY_MAINNET — operator relay URL for mainnet (optional)
|
|
19
|
-
# VITE_WALRUS_RPC_TESTNET — gRPC fullnode for Walrus testnet (optional)
|
|
20
|
-
# VITE_WALRUS_RPC_MAINNET — gRPC fullnode for Walrus mainnet (optional)
|
|
21
|
-
# VITE_RPC_TESTNET / VITE_RPC_MAINNET — override gRPC fullnode URLs (optional)
|
|
22
|
-
# VITE_ACCESS_GATE_ID_{NET} — operator's Gate shared object ID (optional)
|
|
23
|
-
# VITE_ACCESS_GATE_NFT_TYPE_{NET} — access NFT type (optional)
|
|
24
|
-
# VITE_ACCESS_GATE_SOULBOUND_{NET} — "true" if soulbound (optional)
|
|
25
|
-
# VITE_ACCESS_GATE_PRICE_MIST_{NET} — purchase price in MIST (optional)
|
|
26
|
-
# VITE_WALRUS_MAX_TIP_MIST — max relay tip cap in MIST (optional)
|
|
27
|
-
# VITE_PUBLIC_URL — canonical origin for OG/JSON-LD (optional)
|
|
28
|
-
#
|
|
29
|
-
# .env.production is gitignored — treasury addresses must be supplied via build args.
|
|
30
|
-
FROM node:24-slim AS build
|
|
31
|
-
|
|
32
|
-
WORKDIR /app
|
|
33
|
-
|
|
34
|
-
COPY package.json package-lock.json ./
|
|
35
|
-
|
|
36
|
-
RUN npm ci
|
|
37
|
-
|
|
38
|
-
COPY . .
|
|
39
|
-
|
|
40
|
-
ARG VITE_NETWORK=testnet
|
|
41
|
-
ARG VITE_FEE_TREASURY_TESTNET
|
|
42
|
-
ARG VITE_FEE_TREASURY_MAINNET
|
|
43
|
-
ARG VITE_FEE_MIST
|
|
44
|
-
ARG VITE_WALRUS_RELAY_TESTNET
|
|
45
|
-
ARG VITE_WALRUS_RELAY_MAINNET
|
|
46
|
-
ARG VITE_WALRUS_RPC_TESTNET
|
|
47
|
-
ARG VITE_WALRUS_RPC_MAINNET
|
|
48
|
-
ARG VITE_RPC_TESTNET
|
|
49
|
-
ARG VITE_RPC_MAINNET
|
|
50
|
-
ARG VITE_ACCESS_GATE_ID_TESTNET
|
|
51
|
-
ARG VITE_ACCESS_GATE_NFT_TYPE_TESTNET
|
|
52
|
-
ARG VITE_ACCESS_GATE_SOULBOUND_TESTNET
|
|
53
|
-
ARG VITE_ACCESS_GATE_PRICE_MIST_TESTNET
|
|
54
|
-
ARG VITE_ACCESS_GATE_ID_MAINNET
|
|
55
|
-
ARG VITE_ACCESS_GATE_NFT_TYPE_MAINNET
|
|
56
|
-
ARG VITE_ACCESS_GATE_SOULBOUND_MAINNET
|
|
57
|
-
ARG VITE_ACCESS_GATE_PRICE_MIST_MAINNET
|
|
58
|
-
ARG VITE_WALRUS_MAX_TIP_MIST
|
|
59
|
-
ARG VITE_PUBLIC_URL
|
|
60
|
-
|
|
61
|
-
ENV VITE_NETWORK=${VITE_NETWORK} \
|
|
62
|
-
VITE_FEE_TREASURY_TESTNET=${VITE_FEE_TREASURY_TESTNET} \
|
|
63
|
-
VITE_FEE_TREASURY_MAINNET=${VITE_FEE_TREASURY_MAINNET} \
|
|
64
|
-
VITE_FEE_MIST=${VITE_FEE_MIST} \
|
|
65
|
-
VITE_WALRUS_RELAY_TESTNET=${VITE_WALRUS_RELAY_TESTNET} \
|
|
66
|
-
VITE_WALRUS_RELAY_MAINNET=${VITE_WALRUS_RELAY_MAINNET} \
|
|
67
|
-
VITE_WALRUS_RPC_TESTNET=${VITE_WALRUS_RPC_TESTNET} \
|
|
68
|
-
VITE_WALRUS_RPC_MAINNET=${VITE_WALRUS_RPC_MAINNET} \
|
|
69
|
-
VITE_RPC_TESTNET=${VITE_RPC_TESTNET} \
|
|
70
|
-
VITE_RPC_MAINNET=${VITE_RPC_MAINNET} \
|
|
71
|
-
VITE_ACCESS_GATE_ID_TESTNET=${VITE_ACCESS_GATE_ID_TESTNET} \
|
|
72
|
-
VITE_ACCESS_GATE_NFT_TYPE_TESTNET=${VITE_ACCESS_GATE_NFT_TYPE_TESTNET} \
|
|
73
|
-
VITE_ACCESS_GATE_SOULBOUND_TESTNET=${VITE_ACCESS_GATE_SOULBOUND_TESTNET} \
|
|
74
|
-
VITE_ACCESS_GATE_PRICE_MIST_TESTNET=${VITE_ACCESS_GATE_PRICE_MIST_TESTNET} \
|
|
75
|
-
VITE_ACCESS_GATE_ID_MAINNET=${VITE_ACCESS_GATE_ID_MAINNET} \
|
|
76
|
-
VITE_ACCESS_GATE_NFT_TYPE_MAINNET=${VITE_ACCESS_GATE_NFT_TYPE_MAINNET} \
|
|
77
|
-
VITE_ACCESS_GATE_SOULBOUND_MAINNET=${VITE_ACCESS_GATE_SOULBOUND_MAINNET} \
|
|
78
|
-
VITE_ACCESS_GATE_PRICE_MIST_MAINNET=${VITE_ACCESS_GATE_PRICE_MIST_MAINNET} \
|
|
79
|
-
VITE_WALRUS_MAX_TIP_MIST=${VITE_WALRUS_MAX_TIP_MIST} \
|
|
80
|
-
VITE_PUBLIC_URL=${VITE_PUBLIC_URL}
|
|
81
|
-
|
|
82
|
-
RUN npm run build
|
|
83
|
-
|
|
84
|
-
# ── runtime stage ─────────────────────────────────────────────────────────────
|
|
85
|
-
# static-server is a minimal Go binary image — no shell, no package manager.
|
|
86
|
-
# SPA_FALLBACK serves index.html for any extensionless path (Vue Router history mode).
|
|
87
|
-
# CACHE_IMMUTABLE_PREFIX matches the /assets/ directory Vite emits with content hashes.
|
|
88
|
-
FROM quay.io/meddleware-org/static-server:0.1.1
|
|
89
|
-
|
|
90
|
-
COPY --from=build /app/dist /app/public
|
|
91
|
-
|
|
92
|
-
ENV SERVE_DIR=/app/public \
|
|
93
|
-
SPA_FALLBACK=true \
|
|
94
|
-
CACHE_IMMUTABLE_PREFIX=/assets/
|
|
95
|
-
|
|
96
|
-
EXPOSE 8080
|
package/eslint.config.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { globalIgnores } from 'eslint/config'
|
|
2
|
-
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
|
|
3
|
-
import pluginVue from 'eslint-plugin-vue'
|
|
4
|
-
import pluginA11y from 'eslint-plugin-vuejs-accessibility'
|
|
5
|
-
|
|
6
|
-
// Flat ESLint config: layers vue-a11y (WAI-ARIA linting for templates) on top of
|
|
7
|
-
// the standard Vue + TypeScript recommended rules, so accessibility regressions
|
|
8
|
-
// are caught in CI.
|
|
9
|
-
export default defineConfigWithVueTs(
|
|
10
|
-
{
|
|
11
|
-
name: 'token-deployer-ui/files-to-lint',
|
|
12
|
-
files: ['**/*.{vue,ts,mts,tsx}'],
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
globalIgnores(['**/dist/**', '**/coverage/**', '**/*.d.ts', 'docs/api/**']),
|
|
16
|
-
|
|
17
|
-
...pluginVue.configs['flat/essential'],
|
|
18
|
-
...pluginA11y.configs['flat/recommended'],
|
|
19
|
-
vueTsConfigs.recommended,
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
name: 'token-deployer-ui/overrides',
|
|
23
|
-
rules: {
|
|
24
|
-
// Accept both valid label-association patterns: a label wrapping its
|
|
25
|
-
// control (nesting) or a label[for] pointing at a control[id]. UiSelect renders a native
|
|
26
|
-
// <select>, so a label nesting it is labelling a control.
|
|
27
|
-
'vuejs-accessibility/label-has-for': [
|
|
28
|
-
'error',
|
|
29
|
-
{ required: { some: ['nesting', 'id'] }, controlComponents: ['UiSelect'] },
|
|
30
|
-
],
|
|
31
|
-
// UiFormField renders the <label for> for the control in its default slot.
|
|
32
|
-
'vuejs-accessibility/form-control-has-label': ['error', { labelComponents: ['UiFormField'] }],
|
|
33
|
-
// role="list" on styled lists is a deliberate Safari/VoiceOver workaround: WebKit drops
|
|
34
|
-
// list semantics when list-style is removed, so the explicit role stays.
|
|
35
|
-
'vuejs-accessibility/no-redundant-roles': 'off',
|
|
36
|
-
// Underscore-prefixed args/vars are an intentional "unused" marker.
|
|
37
|
-
'@typescript-eslint/no-unused-vars': [
|
|
38
|
-
'error',
|
|
39
|
-
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
|
40
|
-
],
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
// Tests use deliberately loose typing for mocks/stubs.
|
|
46
|
-
name: 'token-deployer-ui/test-overrides',
|
|
47
|
-
files: ['**/*.{test,spec}.{ts,tsx}', 'tests/**', 'e2e/**'],
|
|
48
|
-
rules: {
|
|
49
|
-
'@typescript-eslint/no-explicit-any': 'off',
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
// ConfigForm intentionally edits a parent-owned reactive FormModel in place
|
|
55
|
-
// (the parent passes `:form` and reads the same object back). Converting to
|
|
56
|
-
// defineModel is deferred; the in-place mutation is the current contract.
|
|
57
|
-
name: 'token-deployer-ui/config-form',
|
|
58
|
-
files: ['src/components/ConfigForm.vue'],
|
|
59
|
-
rules: {
|
|
60
|
-
'vue/no-mutating-props': 'off',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
)
|
package/index.html
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
|
6
|
-
<link rel="icon" href="/favicon.ico" sizes="any">
|
|
7
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
|
-
<title>Token Deployer · MeddleWare</title>
|
|
9
|
-
<meta name="description" content="Create your own Sui coin in the browser — your wallet signs and pays gas, nothing is compiled or signed on a server, and you keep the full, verifiable source package.">
|
|
10
|
-
<link rel="canonical" href="%VITE_PUBLIC_URL%/">
|
|
11
|
-
<meta property="og:type" content="website">
|
|
12
|
-
<meta property="og:title" content="Token Deployer · MeddleWare">
|
|
13
|
-
<meta property="og:description" content="Create your own Sui coin in the browser — no server, no custody, full source included.">
|
|
14
|
-
<meta property="og:url" content="%VITE_PUBLIC_URL%/">
|
|
15
|
-
<meta property="og:image" content="%VITE_PUBLIC_URL%/og-image.png">
|
|
16
|
-
<meta name="twitter:card" content="summary_large_image">
|
|
17
|
-
</head>
|
|
18
|
-
<body>
|
|
19
|
-
<div id="app"></div>
|
|
20
|
-
<script type="module" src="/src/main.ts"></script>
|
|
21
|
-
</body>
|
|
22
|
-
</html>
|
package/playwright.config.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { defineConfig, devices } from '@playwright/test'
|
|
2
|
-
|
|
3
|
-
// Mocked-RPC UI e2e. Standardised on Playwright (robust; replaces the former
|
|
4
|
-
// Cypress mocked suite, which exits SIGILL in the sandbox). The app is built with
|
|
5
|
-
// VITE_E2E=1 so main.ts registers the mock wallet + fetch mock (tree-shaken from
|
|
6
|
-
// prod). Real-chain e2e stays in scripts/e2e-deploy.mjs (npm run e2e:localnet…).
|
|
7
|
-
const PORT = 4173
|
|
8
|
-
|
|
9
|
-
export default defineConfig({
|
|
10
|
-
testDir: './e2e',
|
|
11
|
-
fullyParallel: true,
|
|
12
|
-
forbidOnly: !!process.env.CI,
|
|
13
|
-
retries: process.env.CI ? 2 : 0,
|
|
14
|
-
reporter: 'list',
|
|
15
|
-
use: {
|
|
16
|
-
baseURL: `http://localhost:${PORT}`,
|
|
17
|
-
viewport: { width: 1280, height: 800 },
|
|
18
|
-
trace: 'on-first-retry',
|
|
19
|
-
},
|
|
20
|
-
webServer: {
|
|
21
|
-
command: `npm run build:e2e && npm run preview -- --port ${PORT} --strictPort`,
|
|
22
|
-
url: `http://localhost:${PORT}/`,
|
|
23
|
-
reuseExistingServer: !process.env.CI,
|
|
24
|
-
timeout: 180_000,
|
|
25
|
-
},
|
|
26
|
-
projects: [
|
|
27
|
-
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
28
|
-
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
29
|
-
],
|
|
30
|
-
})
|
package/postcss.config.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// Autoprefixer runs over the library's shipped CSS (base.css) during `vite build`,
|
|
2
|
-
// using the `browserslist` field in package.json as the target. Consumers therefore
|
|
3
|
-
// receive vendor-prefixed component CSS without needing their own prefixing step.
|
|
4
|
-
import autoprefixer from 'autoprefixer'
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
plugins: [autoprefixer()],
|
|
8
|
-
}
|
package/public/CNAME
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
sui-token-deployer.meddleware.co.uk
|
package/public/_headers
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# Cloudflare Pages headers. Security baseline; connect-src stays broad because
|
|
2
|
-
# wallet extensions, Walrus relays, and per-network Sui RPCs vary.
|
|
3
|
-
/*
|
|
4
|
-
X-Content-Type-Options: nosniff
|
|
5
|
-
Referrer-Policy: strict-origin-when-cross-origin
|
|
6
|
-
X-Frame-Options: DENY
|
|
7
|
-
Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=()
|
|
8
|
-
Content-Security-Policy: default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https: wss:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
|
|
9
|
-
|
|
10
|
-
# Long-cache hashed build assets.
|
|
11
|
-
/assets/*
|
|
12
|
-
Cache-Control: public, max-age=31536000, immutable
|
package/public/_redirects
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/* /index.html 200
|
package/public/favicon.ico
DELETED
|
Binary file
|
package/public/favicon.svg
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="Token Deployer">
|
|
2
|
-
<circle cx="16" cy="16" r="15" fill="#2f6feb"/>
|
|
3
|
-
<circle cx="16" cy="16" r="15" fill="none" stroke="#1b4fc0" stroke-width="2"/>
|
|
4
|
-
<text x="16" y="21" font-family="system-ui, sans-serif" font-size="14" font-weight="700" fill="#fff" text-anchor="middle">S</text>
|
|
5
|
-
</svg>
|
package/public/og-image.png
DELETED
|
Binary file
|
package/public/robots.txt
DELETED
package/public/sitemap.xml
DELETED
package/scripts/e2e-browser.mjs
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// Thin wrapper: localnet deploy e2e. The real implementation is the network-parametrized
|
|
2
|
-
// `e2e-deploy.mjs`; this just pins E2E_NETWORK=localnet so the historical entry point and its
|
|
3
|
-
// localnet defaults (fresh keypair + faucet, 0xfee… treasury, 1 SUI fee assertion) keep working.
|
|
4
|
-
//
|
|
5
|
-
// Prereq (as before): build the app with the testnet RPC pointed at the local node and a
|
|
6
|
-
// treasury set, then serve dist on APP_URL (default http://localhost:4173):
|
|
7
|
-
// VITE_RPC_TESTNET=http://127.0.0.1:9000 VITE_FEE_TREASURY_TESTNET=<addr> vite build
|
|
8
|
-
//
|
|
9
|
-
// Equivalent to `E2E_NETWORK=localnet node scripts/e2e-deploy.mjs` — or just `npm run e2e:localnet`.
|
|
10
|
-
|
|
11
|
-
process.env.E2E_NETWORK = 'localnet'
|
|
12
|
-
// Preserve the legacy FEE_ADDR override name for anyone still setting it.
|
|
13
|
-
if (process.env.FEE_ADDR && !process.env.E2E_FEE_ADDR) process.env.E2E_FEE_ADDR = process.env.FEE_ADDR
|
|
14
|
-
|
|
15
|
-
await import('./e2e-deploy.mjs')
|