@meddleware/token-deployer-ui 0.0.16 → 0.0.18

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.
@@ -1,317 +0,0 @@
1
- // Client-side generator for the downloadable source package. Mirrors the CLI
2
- // generator (blockchain/sui/sui-token-template/scripts/03_create_token.sh) so the
3
- // zip a user downloads matches what the CLI would produce (and the on-chain
4
- // bytecode). Template text is synced verbatim via scripts/sync-template-src.sh.
5
- //
6
- // Published.toml: since Sui v1.63, the CLI writes deployed addresses to Published.toml
7
- // (not Move.lock). Because the deployer bypasses the CLI, we generate it when a
8
- // PublishResult is available so the downloaded package is immediately usable as a
9
- // dependency reference without requiring the user to reconstruct the file manually.
10
-
11
- import { zipSync, strToU8 } from 'fflate'
12
- import templateFiles from '../template-src/files.json'
13
- import type { PublishResult, TokenConfig } from './types.js'
14
- import {
15
- SAFE_TEXT,
16
- deriveStructName,
17
- validateIdentifier,
18
- hasAllowedIconScheme,
19
- } from './validation.js'
20
-
21
- const files = templateFiles as Record<string, string>
22
-
23
- /** License identifiers may only contain SPDX-safe characters. */
24
- const LICENSE_ID = /^[A-Za-z0-9.\-+]*$/
25
-
26
- /**
27
- * Defence in depth: re-assert the invariants the form validator enforces before
28
- * substituting any user value into Move source or the publish shell script. A
29
- * value that fails here (e.g. a newline or quote that could break out of a Move
30
- * `b"..."` literal) throws rather than producing a malformed/injectable package,
31
- * so the generator is safe even if called with an unvalidated config.
32
- */
33
- function assertSafeConfig(cfg: TokenConfig): void {
34
- const idFields: Array<[string, string]> = [
35
- ['package name', cfg.packageName],
36
- ['module name', cfg.moduleName],
37
- ]
38
- for (const [label, value] of idFields) {
39
- const err = validateIdentifier(value)
40
- if (err) throw new Error(`Invalid ${label}: ${err}`)
41
- }
42
- if (cfg.structName !== deriveStructName(cfg.moduleName)) {
43
- throw new Error('Invalid struct name: must be the module name uppercased.')
44
- }
45
- const textFields: Array<[string, string]> = [
46
- ['symbol', cfg.symbol],
47
- ['name', cfg.name],
48
- ['description', cfg.description],
49
- ['icon URL', cfg.iconUrl],
50
- ]
51
- for (const [label, value] of textFields) {
52
- if (!SAFE_TEXT.test(value)) {
53
- throw new Error(`Invalid ${label}: contains quotes, backslashes or control characters.`)
54
- }
55
- }
56
- // Re-assert the icon-URL scheme allowlist here too (layered defence, independent of the
57
- // form): a non-empty icon URL must use https:// or ipfs:// before it is baked into metadata.
58
- if (cfg.iconUrl && !hasAllowedIconScheme(cfg.iconUrl)) {
59
- throw new Error('Invalid icon URL: only https:// or ipfs:// URLs are allowed.')
60
- }
61
- if (!Number.isInteger(cfg.decimals) || cfg.decimals < 0 || cfg.decimals > 18) {
62
- throw new Error('Invalid decimals: expected a whole number between 0 and 18.')
63
- }
64
- if (!LICENSE_ID.test(cfg.license)) {
65
- throw new Error('Invalid license identifier.')
66
- }
67
- }
68
-
69
- /** Escape a string for safe use as a literal RegExp. */
70
- function esc(s: string): string {
71
- return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
72
- }
73
-
74
- /** Replace every occurrence of a plain-string needle. */
75
- function replaceAll(input: string, needle: string, value: string): string {
76
- return input.replace(new RegExp(esc(needle), 'g'), () => value)
77
- }
78
-
79
- /** Apply the shared X…X documentation placeholders. */
80
- function applyDocPlaceholders(input: string, cfg: TokenConfig): string {
81
- const map: Record<string, string> = {
82
- XPACKAGENAMEX: cfg.packageName,
83
- XMODULENAMEX: cfg.moduleName,
84
- XSTRUCTNAMEX: cfg.structName,
85
- XSYMBOLX: cfg.symbol,
86
- XNAMEX: cfg.name,
87
- XDESCRIPTIONX: cfg.description,
88
- XDECIMALSX: String(cfg.decimals),
89
- XPROJECTNAMEX: cfg.projectName,
90
- XPACKAGEDESCRIPTIONX: cfg.packageDescription,
91
- }
92
- let out = input
93
- for (const [k, v] of Object.entries(map)) out = replaceAll(out, k, v)
94
- return out
95
- }
96
-
97
- // Chain IDs for the two production networks (hex short form).
98
- const CHAIN_IDS: Record<string, string> = {
99
- testnet: '4c78adac',
100
- mainnet: '35834a8a',
101
- }
102
-
103
- // Sui CLI version that compiled the shipped template bytecode. Keep in sync
104
- // with TEMPLATE_MODULE_B64 in src/move-template/template.ts; updated by regen-template.sh.
105
- const TEMPLATE_TOOLCHAIN_VERSION = '1.76.1'
106
-
107
- const isProprietary = (license: string) =>
108
- ['none', 'unlicensed', 'noassertion', ''].includes(license.trim().toLowerCase())
109
-
110
- function spdxId(license: string): string {
111
- return isProprietary(license) ? 'UNLICENSED' : license.trim()
112
- }
113
-
114
- /** Move.toml: package name + license field. */
115
- function renderMoveToml(cfg: TokenConfig): string {
116
- let out = replaceAll(files['Move.toml'], 'sui_token_template', cfg.packageName)
117
- out = out.replace(/^license = .*$/m, `license = "${spdxId(cfg.license)}"`)
118
- return out
119
- }
120
-
121
- /** The Move source: identifiers, named constants, and the license header. */
122
- function renderSource(cfg: TokenConfig): string {
123
- let out = files['source.move']
124
-
125
- // license header (lines 1-2) — always rewritten, whatever licence the template itself carries
126
- const lines = out.split('\n')
127
- if (isProprietary(cfg.license)) {
128
- lines[0] = '// SPDX-License-Identifier: UNLICENSED'
129
- lines[1] = '// All rights reserved. Proprietary and not licensed for redistribution.'
130
- } else {
131
- lines[0] = `// SPDX-License-Identifier: ${spdxId(cfg.license)}`
132
- lines[1] = `// Licensed under the ${spdxId(cfg.license)} license; see the LICENSE file.`
133
- }
134
- out = lines.join('\n')
135
-
136
- out = replaceAll(
137
- out,
138
- 'module sui_token_template::sui_token_template;',
139
- `module ${cfg.packageName}::${cfg.moduleName};`,
140
- )
141
- out = replaceAll(out, 'SUI_TOKEN_TEMPLATE', cfg.structName)
142
- out = replaceAll(out, 'XPACKAGEDESCRIPTIONX', cfg.packageDescription)
143
- out = replaceAll(out, 'XMODULENAMEX', cfg.moduleName)
144
-
145
- out = replaceAll(out, 'const DECIMALS: u8 = 9;', `const DECIMALS: u8 = ${cfg.decimals};`)
146
- out = replaceAll(out, 'b"TEMPLATE_SYMBOL"', `b"${cfg.symbol}"`)
147
- out = replaceAll(out, 'b"TEMPLATE_NAME"', `b"${cfg.name}"`)
148
- out = replaceAll(out, 'b"TEMPLATE_DESCRIPTION"', `b"${cfg.description}"`)
149
- out = replaceAll(out, 'b"TEMPLATE_ICON_URL"', `b"${cfg.iconUrl}"`)
150
- return out
151
- }
152
-
153
- function renderPublishScript(cfg: TokenConfig): string {
154
- let out = replaceAll(files['scripts/publish.sh'], 'SUI_TOKEN_TEMPLATE', cfg.structName)
155
- out = replaceAll(out, 'XMODULENAMEX', cfg.moduleName)
156
- out = replaceAll(out, 'XPACKAGENAMEX', cfg.packageName)
157
- return out
158
- }
159
-
160
- /** The template README's licence line, whatever licence the template itself ships with. */
161
- const TEMPLATE_LICENSE_LINE = /^(?:CC0 1\.0 Universal|BSD Zero Clause License).*$/m
162
-
163
- function renderReadme(cfg: TokenConfig): string {
164
- const out = applyDocPlaceholders(files['README.md'], cfg)
165
- const licenseName = cfg.licenseName || spdxId(cfg.license)
166
- return out.replace(
167
- TEMPLATE_LICENSE_LINE,
168
- isProprietary(cfg.license)
169
- ? 'All rights reserved. This package is proprietary and not licensed for redistribution.'
170
- : `${licenseName} — see the LICENSE file.`,
171
- )
172
- }
173
-
174
- /**
175
- * Replace a single table row in a markdown section in-place.
176
- * Matches: `| <fieldName> | \`FILL_IN_AFTER_DEPLOY\` |`
177
- */
178
- function fillRow(section: string, fieldName: string, value: string): string {
179
- return section.replace(
180
- `| ${fieldName} | \`FILL_IN_AFTER_DEPLOY\` |`,
181
- `| ${fieldName} | \`${value}\` |`,
182
- )
183
- }
184
-
185
- /**
186
- * Fill the deployment table rows for the specified network section only.
187
- * Operates on the substring between the section header and the next `## ` heading.
188
- */
189
- function fillNetworkSection(doc: string, network: string, result: PublishResult): string {
190
- const header = `## ${network.charAt(0).toUpperCase() + network.slice(1)}\n`
191
- const start = doc.indexOf(header)
192
- if (start === -1) return doc
193
- const afterHeader = start + header.length
194
- const nextSection = doc.indexOf('\n## ', afterHeader)
195
- const end = nextSection === -1 ? doc.length : nextSection
196
-
197
- let section = doc.slice(start, end)
198
-
199
- section = fillRow(section, 'Package ID', result.packageId)
200
- if (result.treasuryCapId) section = fillRow(section, 'TreasuryCap ID', result.treasuryCapId)
201
- if (result.metadataCapId) section = fillRow(section, 'MetadataCap ID', result.metadataCapId)
202
- if (result.currencyId) section = fillRow(section, 'Currency object ID', result.currencyId)
203
-
204
- if (result.upgradeCapId) {
205
- section = fillRow(section, 'UpgradeCap ID (burned)', result.upgradeCapId)
206
- section = section.replace(
207
- '| Immutability confirmed | Yes (Y/N) |',
208
- '| Immutability confirmed | No (pending) |',
209
- )
210
- } else {
211
- section = fillRow(section, 'UpgradeCap ID (burned)', 'burned')
212
- section = section.replace(
213
- '| Immutability confirmed | Yes (Y/N) |',
214
- '| Immutability confirmed | Yes |',
215
- )
216
- }
217
-
218
- const deployed = new Date().toISOString().replace('T', ' ').split('.')[0] + ' UTC'
219
- section = fillRow(section, 'Deployed', deployed)
220
-
221
- return doc.slice(0, start) + section + doc.slice(end)
222
- }
223
-
224
- /** Optionally fill deployments.md with the just-published IDs. */
225
- function renderDeployments(cfg: TokenConfig, result?: PublishResult): string {
226
- let out = applyDocPlaceholders(files['deployments.md'], cfg)
227
- if (!result) return out
228
-
229
- // Fill the network-specific table section with known artefact IDs
230
- out = fillNetworkSection(out, result.network, result)
231
-
232
- // Fill the <PACKAGE_ID> placeholder in the downstream Move.toml reference block
233
- out = out.replace(/<PACKAGE_ID>/g, result.packageId)
234
-
235
- // Append extra data not represented in the table (coin type, digest)
236
- const note = [
237
- '',
238
- `<!-- Auto-filled by the Token Deployer on ${new Date().toISOString()} (${result.network}) -->`,
239
- `<!-- Coin type: ${result.coinType} -->`,
240
- `<!-- Publish digest: ${result.digest} -->`,
241
- '',
242
- ].join('\n')
243
- return `${out.trimEnd()}\n${note}`
244
- }
245
-
246
- /**
247
- * Generate a Published.toml for the deployed package.
248
- * Since v1.63, the Sui CLI writes Published.toml (not Move.lock) to record deployed
249
- * addresses per environment. The deployer bypasses the CLI, so we produce this file
250
- * so the downloaded package is immediately usable as a Move dependency reference.
251
- * Commit Published.toml to source control; add Pub.*.toml (ephemeral) to .gitignore.
252
- */
253
- function renderPublishedToml(result: PublishResult): string {
254
- const env = result.network === 'mainnet' ? 'mainnet' : 'testnet'
255
- const chainId = CHAIN_IDS[env] ?? ''
256
- const lines = [
257
- `[published.${env}]`,
258
- `chain-id = "${chainId}"`,
259
- `original-id = "${result.packageId}"`,
260
- `published-at = "${result.packageId}"`,
261
- `version = 1`,
262
- `toolchain-version = "${TEMPLATE_TOOLCHAIN_VERSION}"`,
263
- `build-config = { flavor = "sui", edition = "2024" }`,
264
- ]
265
- if (result.upgradeCapId) {
266
- lines.push(`upgrade-capability = "${result.upgradeCapId}"`)
267
- } else {
268
- lines.push(
269
- `# upgrade-capability burned via 0x2::package::make_immutable — package is immutable`,
270
- )
271
- }
272
- return lines.join('\n') + '\n'
273
- }
274
-
275
- export interface GeneratePackageOptions {
276
- config: TokenConfig
277
- /** Full license text; when omitted for a real license, no LICENSE file is written. */
278
- licenseText?: string
279
- /** Publish result, to pre-fill deployments.md. */
280
- result?: PublishResult
281
- }
282
-
283
- /** Build the in-memory file map for the generated package. */
284
- export function buildPackageFiles(opts: GeneratePackageOptions): Record<string, string> {
285
- const { config, licenseText } = opts
286
- assertSafeConfig(config)
287
- const out: Record<string, string> = {
288
- 'Move.toml': renderMoveToml(config),
289
- [`sources/${config.moduleName}.move`]: renderSource(config),
290
- 'scripts/publish.sh': renderPublishScript(config),
291
- '.gitignore': files['.gitignore'],
292
- 'README.md': renderReadme(config),
293
- 'deployments.md': renderDeployments(config, opts.result),
294
- 'CLAUDE.md': applyDocPlaceholders(files['CLAUDE.md'], config),
295
- 'AGENTS.md': applyDocPlaceholders(files['AGENTS.md'], config),
296
- }
297
- if (opts.result) {
298
- // Published.toml records the deployed address per environment (Sui v1.63+ convention).
299
- // Commit this file to source control; add Pub.*.toml (ephemeral) to .gitignore.
300
- out['Published.toml'] = renderPublishedToml(opts.result)
301
- }
302
- if (!isProprietary(config.license) && licenseText) {
303
- out['LICENSE'] = licenseText
304
- }
305
- return out
306
- }
307
-
308
- /** Zip the generated package. Returns bytes suitable for a download Blob. */
309
- export function generatePackageZip(opts: GeneratePackageOptions): Uint8Array {
310
- const filesMap = buildPackageFiles(opts)
311
- const zipInput: Record<string, Uint8Array> = {}
312
- const root = opts.config.packageName
313
- for (const [path, content] of Object.entries(filesMap)) {
314
- zipInput[`${root}/${path}`] = strToU8(content)
315
- }
316
- return zipSync(zipInput, { level: 6 })
317
- }
@@ -1,41 +0,0 @@
1
- // Discover coins the connected wallet has deployed, by listing the TreasuryCap<T> objects it
2
- // owns (the deploy sends the TreasuryCap to the sender). Read-only; mirrors access-gate's
3
- // AdminCap-based "My Gates" discovery.
4
- import { getReadClient } from './readClient.js'
5
- import type { Network } from './types.js'
6
-
7
- /** One deployed coin the wallet controls (holds the TreasuryCap for). */
8
- export interface DeployedToken {
9
- /** Full coin type, e.g. `0x<pkg>::mytoken::MYTOKEN`. */
10
- coinType: string
11
- /** The published package id (first segment of the coin type). */
12
- packageId: string
13
- /** Object id of the owned `TreasuryCap<T>`. */
14
- treasuryCapId: string
15
- /** The one-time-witness struct name (last segment) — a compact display label. */
16
- label: string
17
- }
18
-
19
- /** List the coins `owner` has deployed on `network` (via owned `TreasuryCap<T>` objects). */
20
- export async function listMyTokens(owner: string, network: Network): Promise<DeployedToken[]> {
21
- const client = getReadClient(network)
22
- // A type filter without type params matches every TreasuryCap<T> instantiation.
23
- const { objects } = await client.listOwnedObjects({ owner, type: '0x2::coin::TreasuryCap' })
24
- const seen = new Set<string>()
25
- const out: DeployedToken[] = []
26
- for (const obj of objects) {
27
- // gRPC may render the framework address in long form (0x000…02).
28
- const m = obj.type.match(/^0x0*2::coin::TreasuryCap<(.+)>$/)
29
- if (!m) continue
30
- const coinType = m[1]
31
- if (seen.has(coinType)) continue
32
- seen.add(coinType)
33
- out.push({
34
- coinType,
35
- packageId: coinType.split('::')[0] ?? '',
36
- treasuryCapId: obj.objectId,
37
- label: coinType.split('::').pop() ?? coinType,
38
- })
39
- }
40
- return out
41
- }
@@ -1,25 +0,0 @@
1
- // Cached gRPC client per network, shared by read-only queries (listOwnedObjects, etc.) and
2
- // the deploy executor. Public fullnodes have deprecated JSON-RPC, so this is a SuiGrpcClient.
3
- import { SuiGrpcClient } from '@mysten/sui/grpc'
4
- import type { Network } from './types.js'
5
- import { RPC_URLS } from '../config.js'
6
-
7
- const clients = new Map<Network, SuiGrpcClient>()
8
-
9
- /** Return a memoised {@link SuiGrpcClient} for the given network. */
10
- export function getReadClient(network: Network): SuiGrpcClient {
11
- let c = clients.get(network)
12
- if (!c) {
13
- c = new SuiGrpcClient({ network, baseUrl: RPC_URLS[network] })
14
- clients.set(network, c)
15
- }
16
- return c
17
- }
18
-
19
- /**
20
- * Replace the client for `network` (E2E builds only — lets `main.ts` inject a stub client in
21
- * place of the network, since gRPC-web responses are binary and cannot be faked via `fetch`).
22
- */
23
- export function setReadClient(network: Network, client: SuiGrpcClient): void {
24
- clients.set(network, client)
25
- }
@@ -1,134 +0,0 @@
1
- // Client-side Move bytecode patching.
2
- //
3
- // The current on-chain binary format (v7) cannot be round-tripped by
4
- // @mysten/move-bytecode-template's `update_identifiers` / `update_constants`
5
- // helpers (they throw "missing field `version`"). However, `deserialize` and
6
- // `serialize` DO handle v7 losslessly, so we patch the decoded JSON directly:
7
- // rename identifiers in place and overwrite constant-pool entries. This is
8
- // verified end-to-end against a localnet publish (see scripts/publish-localnet.mjs).
9
-
10
- import init, { deserialize, serialize } from '@mysten/move-bytecode-template'
11
- import { bcs } from '@mysten/bcs'
12
- import { fromBase64 } from '@mysten/sui/utils'
13
- import { SAFE_TEXT } from './validation.js'
14
- import {
15
- TEMPLATE_MODULE_B64,
16
- TEMPLATE_IDENTIFIERS,
17
- TEMPLATE_DEFAULTS,
18
- } from '../move-template/template.js'
19
-
20
- let initPromise: Promise<unknown> | null = null
21
- let wasmInput: unknown = undefined
22
-
23
- /**
24
- * Register the wasm asset URL without loading it (browser). Call once at app
25
- * startup, e.g. `configureWasm(wasmUrl)`. The actual fetch is deferred until the
26
- * first patch, so the 343 kB wasm doesn't block initial page load. In Node/tests
27
- * this is unnecessary — the nodejs build loads its own wasm.
28
- */
29
- export function configureWasm(input: unknown): void {
30
- wasmInput = input
31
- }
32
-
33
- /**
34
- * Initialise the wasm module (idempotent + cached). Lazily triggered by the
35
- * patch pipeline; uses the URL registered via `configureWasm` in the browser.
36
- */
37
- export function initTemplateWasm(): Promise<unknown> {
38
- if (!initPromise) {
39
- initPromise = Promise.resolve()
40
- .then(() => (init as unknown as (i?: unknown) => unknown)(wasmInput))
41
- // Deliberate: under Node the package's nodejs build loads its own wasm and this init call
42
- // is unnecessary (and may throw). A genuine browser init failure is not hidden — the first
43
- // bytecode call in patchTemplateModule then fails with the wasm error.
44
- .catch(() => undefined)
45
- }
46
- return initPromise
47
- }
48
-
49
- /** BCS bytes for a `vector<u8>` / `String` constant (uleb length + utf8). */
50
- function vecU8(value: string): number[] {
51
- return Array.from(bcs.string().serialize(value).toBytes())
52
- }
53
-
54
- function sameBytes(a: number[], b: number[]): boolean {
55
- return a.length === b.length && a.every((x, i) => x === b[i])
56
- }
57
-
58
- export interface PatchParams {
59
- moduleName: string
60
- structName: string
61
- symbol: string
62
- name: string
63
- description: string
64
- iconUrl: string
65
- decimals: number
66
- }
67
-
68
- interface DecodedConstant {
69
- type_: 'U8' | { Vector: 'U8' } | unknown
70
- data: number[]
71
- }
72
-
73
- interface DecodedModule {
74
- identifiers: string[]
75
- constant_pool: DecodedConstant[]
76
- }
77
-
78
- /**
79
- * Patch the template module with the caller's token parameters and return
80
- * publishable module bytes. Pure with respect to the shipped artifact — a fresh
81
- * copy is decoded each call.
82
- */
83
- export async function patchTemplateModule(params: PatchParams): Promise<Uint8Array> {
84
- await initTemplateWasm()
85
-
86
- const original = fromBase64(TEMPLATE_MODULE_B64)
87
- const json = deserialize(original) as unknown as DecodedModule
88
-
89
- // 1) Rename the module + struct identifiers in place (index-preserving, so all
90
- // handle references stay valid; the verifier does not require sorted ids).
91
- const rename: Record<string, string> = {
92
- [TEMPLATE_IDENTIFIERS.module]: params.moduleName,
93
- [TEMPLATE_IDENTIFIERS.struct]: params.structName,
94
- }
95
- json.identifiers = json.identifiers.map((id) => rename[id] ?? id)
96
-
97
- // 2) Overwrite the five constant-pool defaults with the requested values.
98
- patchU8Constant(json, TEMPLATE_DEFAULTS.decimals, params.decimals)
99
- patchVecConstant(json, TEMPLATE_DEFAULTS.symbol, params.symbol)
100
- patchVecConstant(json, TEMPLATE_DEFAULTS.name, params.name)
101
- patchVecConstant(json, TEMPLATE_DEFAULTS.description, params.description)
102
- patchVecConstant(json, TEMPLATE_DEFAULTS.iconUrl, params.iconUrl)
103
-
104
- return new Uint8Array(serialize(json as unknown as Parameters<typeof serialize>[0]))
105
- }
106
-
107
- function patchU8Constant(json: DecodedModule, current: number, next: number): void {
108
- const entry = json.constant_pool.find(
109
- (c) => c.type_ === 'U8' && c.data.length === 1 && c.data[0] === current,
110
- )
111
- if (!entry) throw new Error(`template U8 constant ${current} not found (artifact drift?)`)
112
- if (next < 0 || next > 255 || !Number.isInteger(next)) {
113
- throw new Error(`decimals must be an integer 0..255, got ${next}`)
114
- }
115
- entry.data = [next]
116
- }
117
-
118
- /** Upper bound on a patched string constant, matching the form's icon-URL cap. */
119
- const MAX_VEC_CONSTANT_LEN = 512
120
-
121
- function patchVecConstant(json: DecodedModule, current: string, next: string): void {
122
- // Defence in depth: the on-chain metadata must match the generated source, so
123
- // reject the same characters the form/source guard rejects, and bound the size.
124
- if (!SAFE_TEXT.test(next)) {
125
- throw new Error('constant contains quotes, backslashes or control characters')
126
- }
127
- if (next.length > MAX_VEC_CONSTANT_LEN) {
128
- throw new Error(`constant exceeds ${MAX_VEC_CONSTANT_LEN} characters`)
129
- }
130
- const currentBytes = vecU8(current)
131
- const entry = json.constant_pool.find((c) => c.type_ !== 'U8' && sameBytes(c.data, currentBytes))
132
- if (!entry) throw new Error(`template constant "${current}" not found (artifact drift?)`)
133
- entry.data = vecU8(next)
134
- }
package/src/lib/types.ts DELETED
@@ -1,69 +0,0 @@
1
- // Shared types for the token deployer.
2
-
3
- /** A Sui network the deployer can target. */
4
- export type Network = 'testnet' | 'mainnet' | 'localnet'
5
-
6
- /** Whether the deployer keeps the ability to mint more supply after launch. */
7
- export type SupplyPolicy = 'fixed' | 'mintable'
8
-
9
- /** Whether description/icon stay updatable (MetadataCap retained) or are frozen. */
10
- export type MetadataPolicy = 'updatable' | 'frozen'
11
-
12
- /** Whether the package code can be upgraded, or is made immutable at publish. */
13
- export type PackagePolicy = 'immutable' | 'upgradeable'
14
-
15
- /**
16
- * The full set of choices a user makes. `identifiers` are baked into the coin
17
- * type and are permanent; `symbol`/`name`/`decimals` are permanent on-chain.
18
- */
19
- export interface TokenConfig {
20
- /** Move package name, e.g. "my_token" (snake_case identifier). */
21
- packageName: string
22
- /** Move module name, e.g. "mytoken" (snake_case identifier). */
23
- moduleName: string
24
- /** One-time-witness struct name, e.g. "MYTOKEN" (SCREAMING_SNAKE identifier). */
25
- structName: string
26
-
27
- symbol: string
28
- name: string
29
- description: string
30
- /** Icon URL (may be a Walrus aggregator URL). Empty string allowed. */
31
- iconUrl: string
32
- /** 0..255; conventionally 9 on Sui. */
33
- decimals: number
34
-
35
- /** Whole-token initial supply minted to the recipient (before decimals scaling). Use 0 for none. */
36
- initialSupply: bigint
37
- supplyPolicy: SupplyPolicy
38
- metadataPolicy: MetadataPolicy
39
- packagePolicy: PackagePolicy
40
-
41
- /** Address that receives the caps + initial supply. Defaults to the connected wallet. */
42
- recipient: string
43
-
44
- /** SPDX id (e.g. "0BSD", "MIT") or "NONE" for proprietary. Affects the downloadable package only. */
45
- license: string
46
- /** Human-readable license name for the README (defaults to the SPDX id). */
47
- licenseName?: string
48
- /** Free-text descriptions used only in generated docs. */
49
- packageDescription: string
50
- projectName: string
51
- }
52
-
53
- /** Result of a successful on-chain publish. */
54
- export interface PublishResult {
55
- network: Network
56
- packageId: string
57
- coinType: string
58
- treasuryCapId?: string
59
- metadataCapId?: string
60
- currencyId?: string
61
- /** Version and digest of the pending Currency<T> object — needed for finalize_registration. */
62
- currencyVersion?: string
63
- currencyDigest?: string
64
- upgradeCapId?: string
65
- digest: string
66
- /** Where the trivial fee was sent, and how much (MIST). */
67
- feeRecipient: string
68
- feeMist: string
69
- }
@@ -1,31 +0,0 @@
1
- // AUTO-GENERATED ARTIFACT — do not edit by hand.
2
- //
3
- // Base64 of the compiled `sui_token_template` Move module (binary format v7),
4
- // produced by `sui move build --build-env testnet` from
5
- // blockchain/sui/sui-token-template. Regenerate with scripts/regen-template.sh
6
- // whenever the canonical template source changes (see move-template/README.md).
7
- //
8
- // The DEFAULT_* values below are the constant-pool defaults compiled into the
9
- // module. The deployer patches the module by matching these exact defaults and
10
- // replacing them (see lib/template.ts). They are DISTINCT on purpose: identical
11
- // Move constants deduplicate into a single unpatchable pool entry.
12
-
13
- export const TEMPLATE_MODULE_B64 =
14
- 'oRzrCwcAAAUKAQAMAgweAyohBEsIBVNaB60B3QEIigNgBuoDVAq+BAUMwwQ6AA8BDgIGAgcCEAIRAAICAAEDBwACBAwBAAEDAAABAAEDAQwBAAEFBQIAAAoAAQABEgMEAAMJCAkBAAMLBgcBAgQMDQEBDAUNCgsAAwUCBQQMBA4CCAAHCAUAAgsEAQgACwIBCAABCgIBCAEBCAAHCQACCAEIAQgBCAEHCAUCCwMBCQALAgEJAAILAwEJAAcIBQELBAEJAAEGCAUBBQELAgEIAAIJAAUBCwQBCAATQ3VycmVuY3lJbml0aWFsaXplcgtNZXRhZGF0YUNhcBJTVUlfVE9LRU5fVEVNUExBVEUGU3RyaW5nC1RyZWFzdXJ5Q2FwCVR4Q29udGV4dARjb2luDWNvaW5fcmVnaXN0cnkLZHVtbXlfZmllbGQIZmluYWxpemUEaW5pdBVuZXdfY3VycmVuY3lfd2l0aF9vdHcPcHVibGljX3RyYW5zZmVyBnNlbmRlcgZzdHJpbmcSc3VpX3Rva2VuX3RlbXBsYXRlCHRyYW5zZmVyCnR4X2NvbnRleHQEdXRmOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIBCQoCEA9URU1QTEFURV9TWU1CT0wKAg4NVEVNUExBVEVfTkFNRQoCFRRURU1QTEFURV9ERVNDUklQVElPTgoCEhFURU1QTEFURV9JQ09OX1VSTAACAQgBAAAAAAIbCwAHAAcBEQEHAhEBBwMRAQcEEQEKATgADAMKATgBDAILAwoBLhEFOAILAgsBLhEFOAMCAAA='
15
-
16
- /** Identifiers compiled into the template module (renamed at patch time). */
17
- export const TEMPLATE_IDENTIFIERS = {
18
- /** module identifier (lowercase) */
19
- module: 'sui_token_template',
20
- /** one-time-witness struct identifier (uppercase) */
21
- struct: 'SUI_TOKEN_TEMPLATE',
22
- } as const
23
-
24
- /** Distinct constant-pool defaults the deployer patches away from. */
25
- export const TEMPLATE_DEFAULTS = {
26
- decimals: 9,
27
- symbol: 'TEMPLATE_SYMBOL',
28
- name: 'TEMPLATE_NAME',
29
- description: 'TEMPLATE_DESCRIPTION',
30
- iconUrl: 'TEMPLATE_ICON_URL',
31
- } as const