@meddleware/token-deployer-ui 0.0.8 → 0.0.10
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/.env.example +8 -11
- package/.github/workflows/e2e-realchain.yml +1 -1
- package/.github/workflows/node-ci.yml +9 -0
- package/.github/workflows/npm-publish.yml +16 -2
- package/.htmlvalidate.mjs +30 -0
- package/CLAUDE.md +14 -7
- package/Dockerfile +1 -1
- package/LICENSE +11 -118
- package/README.md +3 -5
- package/eslint.config.ts +63 -0
- package/index.html +11 -2
- package/package.json +32 -10
- package/postcss.config.js +8 -0
- package/public/og-image.png +0 -0
- package/public/robots.txt +1 -1
- package/public/sitemap.xml +1 -1
- package/scripts/e2e-deploy.mjs +22 -17
- package/scripts/e2e-localnet.mjs +14 -12
- package/scripts/e2e-walrus-browser.mjs +1 -4
- package/scripts/gen-og-image.mjs +2 -2
- package/scripts/publish-localnet.mjs +5 -4
- package/src/App.vue +8 -6
- package/src/component-styles.css +5 -38
- package/src/components/ConfigForm.vue +74 -28
- package/src/components/DeployProgress.vue +2 -4
- package/src/components/GithubPush.vue +22 -7
- package/src/components/IconPicker.vue +103 -42
- package/src/components/LicensePicker.vue +66 -66
- package/src/components/MyTokens.vue +125 -0
- package/src/components/ResultPanel.vue +2 -4
- package/src/components/ReviewPanel.vue +1 -1
- package/src/components/TermsDialog.vue +28 -27
- package/src/components/TokenDeployerView.vue +109 -40
- package/src/components/WalletBar.vue +67 -78
- package/src/components/WalrusBlobBrowser.vue +33 -25
- package/src/composables/useAccessGate.ts +7 -2
- package/src/composables/useWalrusRelay.ts +1 -1
- package/src/config.ts +10 -13
- package/src/lib/accessGate.ts +28 -14
- package/src/lib/buildPublishTx.ts +1 -1
- package/src/lib/deploy.ts +36 -0
- package/src/lib/deployExecutor.ts +14 -12
- package/src/lib/generatePackage.ts +18 -13
- package/src/lib/licenses.ts +4 -3
- package/src/lib/listMyTokens.ts +41 -0
- package/src/lib/readClient.ts +15 -7
- package/src/lib/types.ts +1 -1
- package/src/lib/validation.ts +8 -4
- package/src/lib/walrus-constants.ts +1 -1
- package/src/lib/walrus.ts +22 -11
- package/src/main.ts +46 -58
- package/src/styles.css +13 -5
- package/src/template-src/files.json +7 -7
- package/stylelint.config.js +33 -0
- package/test-results/.last-run.json +1 -1
- package/tests/accessGate.test.ts +26 -21
- package/tests/deploy.test.ts +70 -2
- package/tests/generatePackage.test.ts +11 -0
- package/tests/licenses.test.ts +6 -3
- package/tests/listMyTokens.test.ts +30 -0
- package/tests/useAccessGate.test.ts +15 -19
- package/typedoc.json +2 -2
- package/vite.config.ts +9 -1
- package/src/components/WalrusIconUpload.vue +0 -205
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { ref, onMounted, computed } from 'vue'
|
|
3
|
-
import { useWallet } from '@meddleware/wallet-adapter'
|
|
4
|
-
import { buildDeployExecutor } from '../lib/deployExecutor.js'
|
|
5
|
-
import { useWalrusRelay } from '../composables/useWalrusRelay.js'
|
|
6
|
-
import { ICON_EPOCHS } from '../lib/walrus-constants.js'
|
|
7
|
-
import { ICON_MAX_BYTES, validateIconFile } from '../config.js'
|
|
8
|
-
import type { WalrusNetwork } from '../lib/walrus.js'
|
|
9
|
-
import type { Network } from '../lib/types.js'
|
|
10
|
-
import { UiNotice } from '@meddleware/ui'
|
|
11
|
-
// Vite serves the Walrus wasm as an asset URL (lightweight import — just the URL).
|
|
12
|
-
import walrusWasmUrl from '@mysten/walrus-wasm/web/walrus_wasm_bg.wasm?url'
|
|
13
|
-
|
|
14
|
-
const props = defineProps<{ network: WalrusNetwork; connected: boolean }>()
|
|
15
|
-
const emit = defineEmits<{ (e: 'uploaded', url: string): void }>()
|
|
16
|
-
|
|
17
|
-
const { account } = useWallet()
|
|
18
|
-
const {
|
|
19
|
-
selectedRelayHost,
|
|
20
|
-
availableRelays,
|
|
21
|
-
estimatedCost,
|
|
22
|
-
fileSizeBytes,
|
|
23
|
-
checkOperatorRelayHealth,
|
|
24
|
-
} = useWalrusRelay(props.network)
|
|
25
|
-
|
|
26
|
-
const uploading = ref(false)
|
|
27
|
-
const status = ref('')
|
|
28
|
-
const error = ref<string | null>(null)
|
|
29
|
-
const fileName = ref('')
|
|
30
|
-
let bytes: Uint8Array | null = null
|
|
31
|
-
|
|
32
|
-
const maxSizeLabel = `${Math.round(ICON_MAX_BYTES / 1024)} KB`
|
|
33
|
-
const ICON_YEARS = Math.round(ICON_EPOCHS / 26)
|
|
34
|
-
|
|
35
|
-
onMounted(() => {
|
|
36
|
-
checkOperatorRelayHealth()
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
const showPublicRelayNotice = computed(() => availableRelays.value.length > 1)
|
|
40
|
-
|
|
41
|
-
const showFreeRelayTip = computed(() => {
|
|
42
|
-
const publicHost = availableRelays.value.find((r) => r.isPublic)?.host
|
|
43
|
-
return showPublicRelayNotice.value && selectedRelayHost.value !== publicHost
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
function onFile(e: Event): void {
|
|
47
|
-
error.value = null
|
|
48
|
-
bytes = null
|
|
49
|
-
fileName.value = ''
|
|
50
|
-
const f = (e.target as HTMLInputElement).files?.[0]
|
|
51
|
-
if (!f) return
|
|
52
|
-
|
|
53
|
-
// UX/cost guard only — the authoritative cap is the edge (relay) body limit.
|
|
54
|
-
const invalid = validateIconFile(f)
|
|
55
|
-
if (invalid) {
|
|
56
|
-
error.value = invalid
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
fileName.value = f.name
|
|
61
|
-
fileSizeBytes.value = f.size
|
|
62
|
-
const reader = new FileReader()
|
|
63
|
-
reader.onload = () => {
|
|
64
|
-
bytes = new Uint8Array(reader.result as ArrayBuffer)
|
|
65
|
-
}
|
|
66
|
-
reader.readAsArrayBuffer(f)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function onRelayChange(newHost: string): void {
|
|
70
|
-
selectedRelayHost.value = newHost
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function upload(): Promise<void> {
|
|
74
|
-
if (!bytes) {
|
|
75
|
-
error.value = 'Choose an image first.'
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
if (!account.value) {
|
|
79
|
-
error.value = 'Connect your wallet first.'
|
|
80
|
-
return
|
|
81
|
-
}
|
|
82
|
-
uploading.value = true
|
|
83
|
-
error.value = null
|
|
84
|
-
try {
|
|
85
|
-
// Lazily load the heavy @mysten/walrus code and Sui client only on demand.
|
|
86
|
-
const { createWalrusClient, createBlobUploadFlow, walrusBlobUrl } =
|
|
87
|
-
await import('../lib/walrus.js')
|
|
88
|
-
const { getReadClient } = await import('../lib/readClient.js')
|
|
89
|
-
const executor = await buildDeployExecutor(props.network as Network)
|
|
90
|
-
const suiClient = getReadClient(props.network as Network)
|
|
91
|
-
const client = createWalrusClient(props.network, {
|
|
92
|
-
wasmUrl: walrusWasmUrl,
|
|
93
|
-
uploadRelayHost: selectedRelayHost.value,
|
|
94
|
-
})
|
|
95
|
-
const flow = createBlobUploadFlow(client, bytes)
|
|
96
|
-
|
|
97
|
-
status.value = 'Encoding…'
|
|
98
|
-
await flow.encode()
|
|
99
|
-
|
|
100
|
-
// Disclose the relay fee before asking for a signature. `calculateUploadRelayTip`
|
|
101
|
-
// takes the UNENCODED size and throws if the tip exceeds the client max guard.
|
|
102
|
-
let feeNote = ''
|
|
103
|
-
try {
|
|
104
|
-
const tipMist = await client.walrus.calculateUploadRelayTip({ size: bytes.length })
|
|
105
|
-
const tipSui = Number(tipMist) / 1e9
|
|
106
|
-
feeNote = tipMist > 0n ? ` Relay fee ≈ ${tipSui.toFixed(4)} SUI + storage (WAL).` : ''
|
|
107
|
-
} catch {
|
|
108
|
-
// Non-fatal for disclosure; the register step surfaces any real tip error.
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
status.value = `Registering blob (approve in wallet)…${feeNote}`
|
|
112
|
-
const regTx = flow.register({
|
|
113
|
-
owner: account.value.address,
|
|
114
|
-
epochs: ICON_EPOCHS,
|
|
115
|
-
deletable: false,
|
|
116
|
-
})
|
|
117
|
-
// Set sender and build with the Sui client to resolve CoinWithBalance intents
|
|
118
|
-
regTx.setSenderIfNotSet(account.value.address)
|
|
119
|
-
await regTx.build({ client: suiClient })
|
|
120
|
-
const reg = await executor.signAndExecute(regTx)
|
|
121
|
-
await executor.waitForTransaction(reg.digest)
|
|
122
|
-
|
|
123
|
-
status.value = 'Uploading to the relay…'
|
|
124
|
-
await flow.upload({ digest: reg.digest })
|
|
125
|
-
|
|
126
|
-
status.value = 'Certifying (approve in wallet)…'
|
|
127
|
-
const certTx = flow.certify()
|
|
128
|
-
// Set sender and build the certify transaction with the Sui client
|
|
129
|
-
certTx.setSenderIfNotSet(account.value.address)
|
|
130
|
-
await certTx.build({ client: suiClient })
|
|
131
|
-
const cert = await executor.signAndExecute(certTx)
|
|
132
|
-
await executor.waitForTransaction(cert.digest)
|
|
133
|
-
|
|
134
|
-
const blob = await flow.getBlob()
|
|
135
|
-
emit('uploaded', walrusBlobUrl(props.network, blob.blobId))
|
|
136
|
-
status.value = 'Uploaded ✓ — URL filled in above.'
|
|
137
|
-
} catch (e) {
|
|
138
|
-
error.value = e instanceof Error ? e.message : String(e)
|
|
139
|
-
status.value = ''
|
|
140
|
-
} finally {
|
|
141
|
-
uploading.value = false
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
</script>
|
|
145
|
-
|
|
146
|
-
<template>
|
|
147
|
-
<div>
|
|
148
|
-
<label for="walrus-file" class="hint" style="display: block; font-weight: 600">
|
|
149
|
-
…or upload an image to Walrus
|
|
150
|
-
</label>
|
|
151
|
-
<div class="row">
|
|
152
|
-
<input
|
|
153
|
-
id="walrus-file"
|
|
154
|
-
type="file"
|
|
155
|
-
accept="image/*"
|
|
156
|
-
aria-label="Choose an image to upload to Walrus"
|
|
157
|
-
aria-describedby="walrus-help"
|
|
158
|
-
style="width: auto"
|
|
159
|
-
@change="onFile"
|
|
160
|
-
/>
|
|
161
|
-
<button type="button" :disabled="uploading || !connected || !fileName" @click="upload">
|
|
162
|
-
<span v-if="uploading" class="spinner" aria-hidden="true"></span>
|
|
163
|
-
Upload to Walrus
|
|
164
|
-
</button>
|
|
165
|
-
</div>
|
|
166
|
-
|
|
167
|
-
<!-- Relay selection (shown only if multiple relays available) -->
|
|
168
|
-
<fieldset v-if="fileName && availableRelays.length > 1" style="margin: 0.75rem 0">
|
|
169
|
-
<legend>Upload relay</legend>
|
|
170
|
-
<div style="display: flex; flex-direction: column; gap: 0.5rem">
|
|
171
|
-
<label
|
|
172
|
-
v-for="option in availableRelays"
|
|
173
|
-
:key="option.host"
|
|
174
|
-
style="display: flex; align-items: center; gap: 0.5rem"
|
|
175
|
-
>
|
|
176
|
-
<input
|
|
177
|
-
type="radio"
|
|
178
|
-
:value="option.host"
|
|
179
|
-
:checked="selectedRelayHost === option.host"
|
|
180
|
-
@change="(e) => onRelayChange((e.target as HTMLInputElement).value)"
|
|
181
|
-
/>
|
|
182
|
-
<span>{{ option.label }}</span>
|
|
183
|
-
</label>
|
|
184
|
-
</div>
|
|
185
|
-
</fieldset>
|
|
186
|
-
|
|
187
|
-
<!-- Cost estimation (shown after file selection) -->
|
|
188
|
-
<UiNotice v-if="fileName && estimatedCost" style="margin: 0.75rem 0">
|
|
189
|
-
<strong>Estimated cost:</strong> {{ estimatedCost.label }}
|
|
190
|
-
</UiNotice>
|
|
191
|
-
|
|
192
|
-
<!-- Public relay notice -->
|
|
193
|
-
<p v-if="showFreeRelayTip" class="hint" style="margin: 0.75rem 0">
|
|
194
|
-
Tip: select the public relay above to avoid the relay fee.
|
|
195
|
-
</p>
|
|
196
|
-
|
|
197
|
-
<p id="walrus-help" class="hint" aria-live="polite">
|
|
198
|
-
Max {{ maxSizeLabel }} · PNG/JPEG/WebP/SVG. Requires <strong>WAL</strong> (storage) and
|
|
199
|
-
<strong>SUI</strong> (gas + a small relay fee) in your wallet; two wallet approvals. Blob
|
|
200
|
-
stored for ~{{ ICON_YEARS }} years.
|
|
201
|
-
<span v-if="status">{{ status }}</span>
|
|
202
|
-
</p>
|
|
203
|
-
<UiNotice v-if="error" type="error">{{ error }}</UiNotice>
|
|
204
|
-
</div>
|
|
205
|
-
</template>
|