@artblocks/abx-cli 0.1.0-alpha.4 → 0.1.0-alpha.40

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.
Files changed (180) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/assets/renderer-scaffold/README.md +29 -11
  3. package/assets/renderer-scaffold/foundry.toml +1 -0
  4. package/assets/renderer-scaffold/remappings.txt +1 -1
  5. package/assets/renderer-scaffold/script/DeployHooks.s.sol +24 -0
  6. package/assets/renderer-scaffold/src/MyHooks.sol +20 -0
  7. package/assets/renderer-scaffold/src/MyRenderer.sol +4 -4
  8. package/assets/renderer-scaffold/src/MyTraits.sol +2 -2
  9. package/assets/renderer-scaffold/test/MyRenderer.t.sol +60 -3
  10. package/dist/bin.d.ts +26 -0
  11. package/dist/bin.d.ts.map +1 -0
  12. package/dist/bin.js +63 -0
  13. package/dist/bin.js.map +1 -0
  14. package/dist/capabilities.d.ts +94 -0
  15. package/dist/capabilities.d.ts.map +1 -0
  16. package/dist/capabilities.js +135 -0
  17. package/dist/capabilities.js.map +1 -0
  18. package/dist/commands/auth.d.ts +54 -0
  19. package/dist/commands/auth.d.ts.map +1 -0
  20. package/dist/commands/auth.js +447 -0
  21. package/dist/commands/auth.js.map +1 -0
  22. package/dist/commands/deploy.d.ts +242 -0
  23. package/dist/commands/deploy.d.ts.map +1 -0
  24. package/dist/commands/deploy.js +4763 -0
  25. package/dist/commands/deploy.js.map +1 -0
  26. package/dist/commands/feedback.d.ts +7 -0
  27. package/dist/commands/feedback.d.ts.map +1 -0
  28. package/dist/commands/feedback.js +147 -0
  29. package/dist/commands/feedback.js.map +1 -0
  30. package/dist/commands/project.d.ts +257 -0
  31. package/dist/commands/project.d.ts.map +1 -0
  32. package/dist/commands/project.js +1414 -0
  33. package/dist/commands/project.js.map +1 -0
  34. package/dist/commands/reads.d.ts +64 -0
  35. package/dist/commands/reads.d.ts.map +1 -0
  36. package/dist/commands/reads.js +701 -0
  37. package/dist/commands/reads.js.map +1 -0
  38. package/dist/commands/scaffold.d.ts +89 -0
  39. package/dist/commands/scaffold.d.ts.map +1 -0
  40. package/dist/commands/scaffold.js +733 -0
  41. package/dist/commands/scaffold.js.map +1 -0
  42. package/dist/commands/service.d.ts +67 -0
  43. package/dist/commands/service.d.ts.map +1 -0
  44. package/dist/commands/service.js +741 -0
  45. package/dist/commands/service.js.map +1 -0
  46. package/dist/commands/storage.d.ts +51 -0
  47. package/dist/commands/storage.d.ts.map +1 -0
  48. package/dist/commands/storage.js +370 -0
  49. package/dist/commands/storage.js.map +1 -0
  50. package/dist/commands/submit-app.d.ts +59 -0
  51. package/dist/commands/submit-app.d.ts.map +1 -0
  52. package/dist/commands/submit-app.js +513 -0
  53. package/dist/commands/submit-app.js.map +1 -0
  54. package/dist/config.d.ts +90 -2
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +284 -11
  57. package/dist/config.js.map +1 -1
  58. package/dist/conformance.d.ts +31 -0
  59. package/dist/conformance.d.ts.map +1 -0
  60. package/dist/conformance.js +390 -0
  61. package/dist/conformance.js.map +1 -0
  62. package/dist/contract-read-error.d.ts +5 -0
  63. package/dist/contract-read-error.d.ts.map +1 -0
  64. package/dist/contract-read-error.js +37 -0
  65. package/dist/contract-read-error.js.map +1 -0
  66. package/dist/deps.d.ts +6 -39
  67. package/dist/deps.d.ts.map +1 -1
  68. package/dist/deps.js +4 -68
  69. package/dist/deps.js.map +1 -1
  70. package/dist/errors.d.ts +20 -0
  71. package/dist/errors.d.ts.map +1 -0
  72. package/dist/errors.js +25 -0
  73. package/dist/errors.js.map +1 -0
  74. package/dist/flag-allowlists.d.ts +53 -0
  75. package/dist/flag-allowlists.d.ts.map +1 -0
  76. package/dist/flag-allowlists.js +180 -0
  77. package/dist/flag-allowlists.js.map +1 -0
  78. package/dist/flags.d.ts +41 -0
  79. package/dist/flags.d.ts.map +1 -1
  80. package/dist/flags.js +111 -1
  81. package/dist/flags.js.map +1 -1
  82. package/dist/jsonout.d.ts +37 -0
  83. package/dist/jsonout.d.ts.map +1 -0
  84. package/dist/jsonout.js +68 -0
  85. package/dist/jsonout.js.map +1 -0
  86. package/dist/kind.d.ts +57 -0
  87. package/dist/kind.d.ts.map +1 -0
  88. package/dist/kind.js +122 -0
  89. package/dist/kind.js.map +1 -0
  90. package/dist/main.js +747 -4838
  91. package/dist/main.js.map +1 -1
  92. package/dist/mintpage.d.ts +17 -2
  93. package/dist/mintpage.d.ts.map +1 -1
  94. package/dist/mintpage.js +241 -54
  95. package/dist/mintpage.js.map +1 -1
  96. package/dist/output.d.ts +179 -0
  97. package/dist/output.d.ts.map +1 -0
  98. package/dist/output.js +780 -0
  99. package/dist/output.js.map +1 -0
  100. package/dist/ownerops.d.ts +312 -57
  101. package/dist/ownerops.d.ts.map +1 -1
  102. package/dist/ownerops.js +1808 -357
  103. package/dist/ownerops.js.map +1 -1
  104. package/dist/preview.d.ts +23 -5
  105. package/dist/preview.d.ts.map +1 -1
  106. package/dist/preview.js +95 -43
  107. package/dist/preview.js.map +1 -1
  108. package/dist/prompt.d.ts +17 -0
  109. package/dist/prompt.d.ts.map +1 -0
  110. package/dist/prompt.js +19 -0
  111. package/dist/prompt.js.map +1 -0
  112. package/dist/provision.d.ts +3 -13
  113. package/dist/provision.d.ts.map +1 -1
  114. package/dist/provision.js +19 -21
  115. package/dist/provision.js.map +1 -1
  116. package/dist/remote.d.ts +157 -52
  117. package/dist/remote.d.ts.map +1 -1
  118. package/dist/remote.js +435 -46
  119. package/dist/remote.js.map +1 -1
  120. package/dist/riskgate.d.ts +62 -0
  121. package/dist/riskgate.d.ts.map +1 -0
  122. package/dist/riskgate.js +234 -0
  123. package/dist/riskgate.js.map +1 -0
  124. package/dist/scaffold.d.ts +12 -0
  125. package/dist/scaffold.d.ts.map +1 -0
  126. package/dist/scaffold.js +56 -0
  127. package/dist/scaffold.js.map +1 -0
  128. package/dist/schema.d.ts +36 -1
  129. package/dist/schema.d.ts.map +1 -1
  130. package/dist/schema.js +121 -26
  131. package/dist/schema.js.map +1 -1
  132. package/dist/script-chunks.d.ts +8 -0
  133. package/dist/script-chunks.d.ts.map +1 -0
  134. package/dist/script-chunks.js +35 -0
  135. package/dist/script-chunks.js.map +1 -0
  136. package/dist/served.d.ts +30 -0
  137. package/dist/served.d.ts.map +1 -0
  138. package/dist/served.js +112 -0
  139. package/dist/served.js.map +1 -0
  140. package/dist/signer.d.ts +13 -0
  141. package/dist/signer.d.ts.map +1 -1
  142. package/dist/signer.js +84 -15
  143. package/dist/signer.js.map +1 -1
  144. package/dist/update-check.d.ts +86 -5
  145. package/dist/update-check.d.ts.map +1 -1
  146. package/dist/update-check.js +161 -20
  147. package/dist/update-check.js.map +1 -1
  148. package/package.json +13 -12
  149. package/skill/SKILL.md +179 -347
  150. package/skill/agents/openai.yaml +4 -0
  151. package/skill/reference/capabilities.md +188 -0
  152. package/skill/reference/code.md +211 -0
  153. package/skill/reference/creator-token.md +94 -0
  154. package/skill/reference/deploy.md +167 -0
  155. package/skill/reference/diagnose.md +165 -0
  156. package/skill/reference/hosting.md +161 -94
  157. package/skill/reference/operate.md +181 -0
  158. package/skill/reference/services.md +121 -0
  159. package/skill/reference/setup.md +140 -36
  160. package/assets/renderer-scaffold/src/interfaces/IAbxFieldRenderer.sol +0 -32
  161. package/assets/renderer-scaffold/src/interfaces/IAbxParams.sol +0 -26
  162. package/dist/inspect.d.ts +0 -48
  163. package/dist/inspect.d.ts.map +0 -1
  164. package/dist/inspect.js +0 -184
  165. package/dist/inspect.js.map +0 -1
  166. package/dist/migrate.d.ts +0 -65
  167. package/dist/migrate.d.ts.map +0 -1
  168. package/dist/migrate.js +0 -180
  169. package/dist/migrate.js.map +0 -1
  170. package/dist/onchain-uri.d.ts +0 -97
  171. package/dist/onchain-uri.d.ts.map +0 -1
  172. package/dist/onchain-uri.js +0 -243
  173. package/dist/onchain-uri.js.map +0 -1
  174. package/dist/upload.d.ts +0 -28
  175. package/dist/upload.d.ts.map +0 -1
  176. package/dist/upload.js +0 -41
  177. package/dist/upload.js.map +0 -1
  178. package/skill/reference/code-projects.md +0 -246
  179. package/skill/reference/operating.md +0 -116
  180. package/skill/reference/troubleshooting.md +0 -28
@@ -0,0 +1,234 @@
1
+ /**
2
+ * The CLI's one risk gate for a write — the choke point every signing path (owner-op or deploy)
3
+ * routes an already-prepared tx through, so `--dry-run` and `--confirm` behave IDENTICALLY no
4
+ * matter which command reached them.
5
+ *
6
+ * Before this module existed, the same three-part shape (preview-and-stop under `--dry-run` →
7
+ * optional interactive `--confirm` → pick a lane → sign) was hand-written THREE times: ownerops.ts's
8
+ * `runWrite` (every owner-op), a near-identical `runMinterWrite` ("the minter path does not go
9
+ * through runWrite, so it needs its OWN dry-run guard" — its own comment conceding the duplication),
10
+ * and a third copy inline in main.ts's `deploy-code --resume` send path. Each could drift from the
11
+ * others without anyone deciding it should — and the two owner-op copies HAD already drifted: only
12
+ * one of them printed the `owner` line on a dry-run preview when the caller supplied one.
13
+ *
14
+ * `gatedSend` is the merge. Callers that had NO `--confirm` support (every owner-op) gain it for
15
+ * free just by routing through here — there is no owner-op-specific reason they lacked it; it was
16
+ * simply never wired.
17
+ */
18
+ import { decodeErrorResult, zeroAddress } from 'viem';
19
+ import { createInterface } from 'node:readline';
20
+ import { assertChainId, envSigningKey, makePublicClient, seriesCodeAbi } from '@artblocks/abx-sdk';
21
+ import { signTx } from './signer.js';
22
+ import { isDryRun } from './flags.js';
23
+ // ── tiny ANSI (kept local — every module here stands alone; see signer.ts/ownerops.ts) ───────
24
+ const C = { reset: '\x1b[0m', dim: '\x1b[2m', bold: '\x1b[1m', green: '\x1b[38;5;115m', orange: '\x1b[38;5;215m' };
25
+ const dim = (s) => `${C.dim}${s}${C.reset}`;
26
+ const bold = (s) => `${C.bold}${s}${C.reset}`;
27
+ /** Resolve the signing lane from flags: `--unsigned` (cold) · `--sign` (wallet) · default hot. The
28
+ * one place this decision is made — every write reads the same three flags the same way. */
29
+ export function laneFromFlags(flags) {
30
+ // Two lane flags at once is always a mistake, and silently picking one is how a caller ends up in a
31
+ // lane they did not choose — an agent writing `--send --sign` from two different help lines gets a
32
+ // browser wallet page when it meant the env key, and reads the wait as a hang. Refuse instead: the
33
+ // precedence order below is an implementation detail, not an interface.
34
+ const named = ['send', 'sign', 'unsigned'].filter((k) => flags[k] !== undefined);
35
+ if (named.length > 1) {
36
+ throw new Error(`pick ONE signing lane, not ${named.length}: ${named.map((n) => `--${n}`).join(' and ')}. ` +
37
+ `--send signs with the hot/env key (the default, so it can also be omitted) · --sign opens a ` +
38
+ `wallet page · --unsigned prints the transaction for you to sign elsewhere.`);
39
+ }
40
+ // PRESENCE, not truthiness. A bare `--sign` parses to `'true'`, but `--sign=` parses to `''`, which
41
+ // is falsy — so a truthiness test sent that caller down the hot-key lane after they explicitly named
42
+ // the wallet one. That is the one direction that matters: asking for a browser approval and getting
43
+ // an unattended key signature instead. Naming a lane selects it, full stop.
44
+ if (named.includes('unsigned'))
45
+ return 'unsigned';
46
+ if (named.includes('sign'))
47
+ return 'sign';
48
+ return 'send';
49
+ }
50
+ /**
51
+ * Refuse a real deploy on the HOT lane when there is no key to sign with — before the command has
52
+ * spent anything.
53
+ *
54
+ * The signing lanes are checked at *signing* time, which is the end of a deploy. Everything that
55
+ * makes a deploy possible happens first, and some of it is neither free nor reversible: `--backend
56
+ * arweave|ipfs|cloud` UPLOADS the creator's art before any transaction is prepared. So a deploy run
57
+ * with no key, and no `--dry-run` to say "preview only", would mint an Arweave identity, publish the
58
+ * art, and only THEN discover it could never have deployed. Under Turbo's free tier the upload
59
+ * *succeeds*, so the art is published permanently — an irreversible side effect from a run that was
60
+ * always going to fail.
61
+ *
62
+ * `--dry-run` guards every signing choke point (`isDryRun`, `gatedSend`); this is the same idea one
63
+ * step earlier, and the rule it enforces is the sibling of that one: **a spend or a publish never
64
+ * precedes the check that this run can be signed at all.** Only the hot lane is checked — `--sign`
65
+ * (browser wallet) and `--unsigned` (offline signer) deliberately have no local key, and a preview
66
+ * spends nothing.
67
+ */
68
+ export function assertLaneCanSign(flags) {
69
+ if (isDryRun(flags))
70
+ return; // a preview neither signs nor spends
71
+ if (laneFromFlags(flags) !== 'send')
72
+ return; // wallet/cold lanes have no local key BY DESIGN
73
+ if (envSigningKey())
74
+ return;
75
+ throw new Error('no signing key, so this run could not be sent — stopping before it uploads or spends anything. ' +
76
+ 'Pick a lane: --sign --for 0x.. (you approve in your own wallet, no key here) · --unsigned (print the tx ' +
77
+ 'for an offline signer) · or set ABX_DEPLOYER_PK in .env for the unattended hot lane. ' +
78
+ 'To preview without any of them, add --dry-run --for 0x...');
79
+ }
80
+ /**
81
+ * Opt-in pre-send confirmation gate (`--confirm`). OFF by default → scripted use is untouched. Even
82
+ * when opted in, it never blocks automation: `--yes` or a non-TTY stdin proceeds silently. Only an
83
+ * interactive `--confirm` prints the summary and waits for a y/N. Aborts on anything but yes.
84
+ */
85
+ export async function confirmSend(summary, flags) {
86
+ if (flags.confirm === undefined)
87
+ return; // not opted in
88
+ if (flags.yes !== undefined || !process.stdin.isTTY)
89
+ return; // don't stall scripts/CI
90
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
91
+ const answer = await new Promise((resolve) => rl.question(`\n ${summary}\n Proceed? [y/N] `, resolve));
92
+ rl.close();
93
+ if (!/^y(es)?$/i.test(answer.trim()))
94
+ throw new Error('aborted at --confirm (nothing sent).');
95
+ }
96
+ async function resolveProvider(provider, signer) {
97
+ return typeof provider === 'function' ? await provider(signer) : provider;
98
+ }
99
+ function printDryRunPreview(prepared, expectedSigner) {
100
+ console.log(`\n ${bold('◆ ' + prepared.summary)} ${dim('(dry run — nothing sent)')}`);
101
+ for (const [k, v] of Object.entries(prepared.fields))
102
+ console.log(` ${dim(k.padEnd(12))} ${v}`);
103
+ console.log(` ${dim('to'.padEnd(12))} ${prepared.to ?? dim('(contract deploy)')}`);
104
+ if (expectedSigner)
105
+ console.log(` ${dim('owner'.padEnd(12))} ${expectedSigner}`);
106
+ }
107
+ /**
108
+ * A revert's own name, when the payload carries one — `RoyaltyTooHigh()` instead of a raw hex dump
109
+ * or "reverted for an unknown reason".
110
+ *
111
+ * Whether we get one is the NODE's choice, not ours: many public endpoints (measured:
112
+ * `base-sepolia-rpc.publicnode.com` and `sepolia.base.org`) answer a failed `eth_call` with a bare
113
+ * `execution reverted` and no data at all. So the no-data case says *that*, rather than implying we
114
+ * looked at a reason and couldn't read it.
115
+ */
116
+ export function revertReason(err) {
117
+ const data = err?.data ?? err?.cause?.data;
118
+ const hex = typeof data === 'string' ? data : data?.data;
119
+ if (typeof hex === 'string' && hex.length >= 10) {
120
+ try {
121
+ const decoded = decodeErrorResult({ abi: seriesCodeAbi, data: hex });
122
+ return `${decoded.errorName}(${(decoded.args ?? []).join(', ')})`;
123
+ }
124
+ catch {
125
+ /* not one of ours — fall through to the message */
126
+ }
127
+ }
128
+ const short = err?.shortMessage ?? err?.message ?? 'reverted';
129
+ // viem's phrasing for "the node sent no revert data" reads like OUR failure to decode. Say what
130
+ // actually happened, and what it almost always means.
131
+ if (/unknown reason/i.test(short)) {
132
+ return 'reverted (this RPC returns no revert data) — usually a precondition the contract enforces: a lock, a cap, or the wrong signer';
133
+ }
134
+ return short;
135
+ }
136
+ /**
137
+ * `eth_call` the prepared transaction against current state, and say what would happen.
138
+ *
139
+ * This is what makes `--dry-run` an *answer* rather than an echo of its own input. It matters most
140
+ * for a **multicall** — `attach`'s batched pairs, `deploy-code`'s setup — because a multicall is one
141
+ * transaction, so simulating it exercises the whole sequence atomically against real state, with no
142
+ * fork and no new surface.
143
+ *
144
+ * Reports **unknown**, never a false green, whenever it cannot actually prove anything: no signer to
145
+ * simulate as (an owner-gated call from nobody reverts for the wrong reason), a contract deploy, a
146
+ * target that does not exist yet, or an unreachable node.
147
+ */
148
+ async function simulateDryRun(prepared, chainKey, expectedSigner, narrate = true) {
149
+ const label = 'simulation'.padEnd(12);
150
+ const unknown = (why) => {
151
+ if (narrate)
152
+ console.log(` ${dim(label)} ${dim(`unknown — ${why}`)}`);
153
+ return { status: 'unknown', reason: why };
154
+ };
155
+ if (!prepared.to)
156
+ return unknown('this creates a contract; there is nothing to call yet');
157
+ if (!expectedSigner || expectedSigner === zeroAddress) {
158
+ return unknown('no signer known to simulate as the caller — set a signing key, or use --sign; some commands ' +
159
+ '(e.g. deploy, predict) also accept --for 0x..');
160
+ }
161
+ // Everything below is best-effort: a dry run must work OFFLINE and against an unknown chain (the
162
+ // preview is the product; the simulation is a bonus). So even constructing the client is guarded —
163
+ // `resolveChain` throws on a chain key it doesn't know, and that must degrade to `unknown`, never
164
+ // turn a preview into a failure.
165
+ try {
166
+ const client = makePublicClient({ chainKey });
167
+ const code = await client.getCode({ address: prepared.to });
168
+ if (!code || code === '0x')
169
+ return unknown(`${prepared.to} has no code yet on this chain`);
170
+ await client.call({
171
+ account: expectedSigner,
172
+ to: prepared.to,
173
+ data: prepared.data,
174
+ ...(prepared.value && prepared.value !== '0x0' ? { value: BigInt(prepared.value) } : {}),
175
+ });
176
+ if (narrate)
177
+ console.log(` ${dim(label)} ${C.green}✓${C.reset} would succeed ${dim('(eth_call against current state)')}`);
178
+ return { status: 'would-succeed' };
179
+ }
180
+ catch (err) {
181
+ // A node that cannot answer is not a failing transaction — do not report one as the other.
182
+ const reason = revertReason(err);
183
+ if (/fetch|network|timeout|ECONN|unknown chain/i.test(reason))
184
+ return unknown(`could not run the simulation (${reason})`);
185
+ if (narrate) {
186
+ console.log(` ${dim(label)} ${C.orange}✗ would REVERT${C.reset} — ${reason}`);
187
+ console.log(` ${dim(''.padEnd(12))} ${dim('sending this now would burn gas and change nothing.')}`);
188
+ }
189
+ return { status: 'would-revert', reason };
190
+ }
191
+ }
192
+ /**
193
+ * The one send choke point for a SINGLE already-prepared write: `--dry-run` preview (nothing sent,
194
+ * returns `null`) → optional `--confirm` prompt → lane selection → `signTx`. Every caller — an
195
+ * owner-op's `runWrite`/`runMinterWrite`, or the deploy family's resumed-setup send — resolves to
196
+ * this, so the two families can never again each carry their own (possibly diverging) idea of what
197
+ * `--dry-run` or `--confirm` means. The historical bug class this forecloses structurally: a write
198
+ * reaching the send lane under `--dry-run` because some caller re-derived the check by hand and
199
+ * missed a branch.
200
+ */
201
+ export async function gatedSend(provider, flags, opts) {
202
+ if (isDryRun(flags)) {
203
+ const prepared = await resolveProvider(provider, opts.expectedSigner ?? zeroAddress);
204
+ if (flags.json !== undefined) {
205
+ const simulation = await simulateDryRun(prepared, opts.chainKey, opts.expectedSigner, false);
206
+ console.log(JSON.stringify({
207
+ dryRun: true,
208
+ sent: false,
209
+ lane: laneFromFlags(flags),
210
+ expectedSigner: opts.expectedSigner ?? null,
211
+ transaction: prepared,
212
+ simulation,
213
+ }, null, 2));
214
+ }
215
+ else {
216
+ printDryRunPreview(prepared, opts.expectedSigner);
217
+ await simulateDryRun(prepared, opts.chainKey, opts.expectedSigner);
218
+ console.log(dim(`\n Re-run without --dry-run to send (lane: ${laneFromFlags(flags)}).\n`));
219
+ }
220
+ return null;
221
+ }
222
+ await assertChainId(opts.chainKey); // verify the RPC really is the target chain before any irreversible write
223
+ const preview = await resolveProvider(provider, opts.expectedSigner ?? zeroAddress);
224
+ await confirmSend(preview.summary, flags);
225
+ return signTx(provider, {
226
+ lane: laneFromFlags(flags),
227
+ chainKey: opts.chainKey,
228
+ expectedSigner: opts.expectedSigner,
229
+ yes: !!flags.yes,
230
+ port: flags.port ? Number(flags.port) : undefined,
231
+ signUrlFile: flags['sign-url-file'],
232
+ });
233
+ }
234
+ //# sourceMappingURL=riskgate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"riskgate.js","sourceRoot":"","sources":["../src/riskgate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /** The file whose presence means the renderer copy genuinely happened (not just `mkdir`). */
2
+ export declare const SCAFFOLD_ANCHOR: string;
3
+ /**
4
+ * Copy the scaffold at `src` into `dir`, omitting build/dependency dirs.
5
+ *
6
+ * Throws if the copy produced no files — the caller must not print a success banner over an empty
7
+ * directory. Returns nothing on success; `anchor` is guaranteed to exist relative to `dir`.
8
+ */
9
+ export declare function copyScaffold(src: string, dir: string, anchor: string): void;
10
+ /** @deprecated thin wrapper kept for the existing call sites/tests — prefer {@link copyScaffold}. */
11
+ export declare function copyRendererScaffold(src: string, dir: string): void;
12
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AA6BA,6FAA6F;AAC7F,eAAO,MAAM,eAAe,QAAgC,CAAC;AAE7D;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAiB3E;AAED,qGAAqG;AACrG,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAEnE"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Copying the bundled renderer scaffold onto a creator's disk.
3
+ *
4
+ * This lives in its own module because the inline version of it shipped broken for six releases
5
+ * (alpha.9 → alpha.14) and could not be reproduced in a dev checkout. `abx scaffold-renderer`
6
+ * created the directory, printed the whole success walkthrough, exited 0, and wrote **zero files** —
7
+ * for every user, on every invocation form, while working perfectly for us.
8
+ *
9
+ * The cause was a path filter that judged absolute paths:
10
+ *
11
+ * filter: (s) => !/(^|\/)(out|cache|dependencies|broadcast|node_modules)(\/|$)/.test(s)
12
+ *
13
+ * `cpSync` hands `filter` ABSOLUTE source paths. An installed CLI lives at
14
+ * `…/node_modules/@artblocks/abx-cli/assets/renderer-scaffold`, so that pattern matched the source
15
+ * ROOT — `cpSync` skips a directory's entire subtree when the directory itself is filtered out, and
16
+ * silently copies nothing rather than erroring. A dev checkout's path
17
+ * (`…/plan/abx/packages/cli/assets/…`) contains no `node_modules`, so every test we ran passed.
18
+ *
19
+ * Two rules follow, and the tests in `test/scaffold.test.ts` pin both:
20
+ * 1. Judge paths RELATIVE to the source root, never the absolute string.
21
+ * 2. Never report success without asserting the output actually landed.
22
+ */
23
+ import { cpSync, existsSync, mkdirSync } from 'node:fs';
24
+ import { join, relative } from 'node:path';
25
+ /** Build/dependency dirs that must never ride along into a fresh scaffold. Matched against the
26
+ * path RELATIVE to the scaffold root — see the module note. */
27
+ const SKIP_DIR = /^(out|cache|dependencies|broadcast|node_modules)(\/|$)/;
28
+ /** The file whose presence means the renderer copy genuinely happened (not just `mkdir`). */
29
+ export const SCAFFOLD_ANCHOR = join('src', 'MyRenderer.sol');
30
+ /**
31
+ * Copy the scaffold at `src` into `dir`, omitting build/dependency dirs.
32
+ *
33
+ * Throws if the copy produced no files — the caller must not print a success banner over an empty
34
+ * directory. Returns nothing on success; `anchor` is guaranteed to exist relative to `dir`.
35
+ */
36
+ export function copyScaffold(src, dir, anchor) {
37
+ mkdirSync(dir, { recursive: true });
38
+ cpSync(src, dir, {
39
+ recursive: true,
40
+ filter: (s) => {
41
+ // Normalise separators so the same pattern holds on Windows.
42
+ const rel = relative(src, s).split(/[/\\]/).join('/');
43
+ return rel === '' || !SKIP_DIR.test(rel);
44
+ },
45
+ });
46
+ if (!existsSync(join(dir, anchor))) {
47
+ throw new Error(`scaffold copy wrote no files into ${dir} (expected ${anchor}) — nothing was written, ` +
48
+ `so don't build here. Scaffold source: ${src}. ` +
49
+ `Please report this along with your install path: https://docs.abx.io`);
50
+ }
51
+ }
52
+ /** @deprecated thin wrapper kept for the existing call sites/tests — prefer {@link copyScaffold}. */
53
+ export function copyRendererScaffold(src, dir) {
54
+ copyScaffold(src, dir, SCAFFOLD_ANCHOR);
55
+ }
56
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AACtD,OAAO,EAAC,IAAI,EAAE,QAAQ,EAAC,MAAM,WAAW,CAAC;AAEzC;gEACgE;AAChE,MAAM,QAAQ,GAAG,wDAAwD,CAAC;AAE1E,6FAA6F;AAC7F,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,GAAW,EAAE,MAAc;IACnE,SAAS,CAAC,GAAG,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE;QACf,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YACZ,6DAA6D;YAC7D,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtD,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,CAAC;KACF,CAAC,CAAC;IACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,qCAAqC,GAAG,cAAc,MAAM,2BAA2B;YACrF,yCAAyC,GAAG,IAAI;YAChD,sEAAsE,CACzE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,oBAAoB,CAAC,GAAW,EAAE,GAAW;IAC3D,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,eAAe,CAAC,CAAC;AAC1C,CAAC"}
package/dist/schema.d.ts CHANGED
@@ -1,15 +1,50 @@
1
- import type { Hex } from 'viem';
1
+ import type { Address, Hex } from 'viem';
2
+ /** The `--schema` type/auth/format catalog, shown wherever the CLI nudges `--schema`. Kept accurate
3
+ * to the on-chain enums (PARAM_TYPES / AUTH_OPTIONS) + the Select-options / Range-bounds format —
4
+ * the old hint advertised non-existent `Anyone`/`Admin` auth legs. Used by both `deploy-code`
5
+ * (commands/deploy.ts) and `inspect` (commands/reads.ts), so it lives here beside the rest of the
6
+ * `--schema` machinery rather than in either command module. */
7
+ export declare const SCHEMA_CATALOG: string;
2
8
  export interface ParsedSchema {
3
9
  key: string;
4
10
  paramType: number;
5
11
  auth: number;
12
+ /** Required by (and only valid for) an Address-bearing auth leg; the contract pairs them strictly. */
13
+ authAddress: Address;
14
+ /** Unix seconds after which the VALUE can no longer change. 0 = never locks. A timestamp in the
15
+ * past locks it immediately and permanently — that is the supported way to retire a parameter. */
16
+ lockAfter: number;
6
17
  min: Hex;
7
18
  max: Hex;
8
19
  selectOptions: string[];
9
20
  }
21
+ /**
22
+ * A point in time for `lock=`: unix seconds, an ISO date, or `now` (which means "immediately and
23
+ * forever" — the retire idiom). Reuses the Timestamp encoder so `lock=2026-12-31` parses exactly
24
+ * like a `Timestamp[..]` bound does; one date grammar for the whole flag.
25
+ */
26
+ export declare function parseWhen(raw: string): number;
10
27
  export declare function parseSchemaSpec(spec: string): ParsedSchema;
11
28
  /** Parse a full `--schema` value: comma-separated specs. */
12
29
  export declare function parseSchemaSpecs(raw: string | undefined): ParsedSchema[];
30
+ /** The advisory an EDITION deploy needs before it commits to a schema, or `null` when there is
31
+ * nothing to say. Returned rather than printed so the deploy lane decides where it lands.
32
+ *
33
+ * Two things are true on an ERC-1155 edition and on nothing else, and a creator setting up their
34
+ * first collector-configurable drop will not have thought about either:
35
+ *
36
+ * 1. **Params are per id, so every holder of that id shares one value.** `TokenOwner` means ANY
37
+ * holder, and it is last-writer-wins. A "name your copy" schema does not name a copy — it
38
+ * renames the work for all 1,000 holders.
39
+ * 2. **A data param has no on-chain size budget.** One holder can store a large valid value under
40
+ * every declared key and make the shared id's `tokenURI` too expensive for common RPC limits —
41
+ * for every co-holder, permanently if a `lockAfter` then bites. That is a deliberate protocol
42
+ * choice (byte accounting on chain would cost every project to police a configuration almost
43
+ * nobody should use), which makes saying it here the actual mitigation.
44
+ *
45
+ * Both are fine when intended: a 1-of-1 edition behaves like a 721, and aggregate state (a counter,
46
+ * a shared mood) is exactly what shared params are good at. */
47
+ export declare function editionSchemaAdvisory(schemas: ParsedSchema[]): string | null;
13
48
  /** A human one-line rendering of a parsed schema (for the confirm readout). */
14
49
  export declare function describeSchema(s: ParsedSchema): string;
15
50
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,MAAM,CAAC;AAO9B,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,CAAC;IACT,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAQD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAyD1D;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,EAAE,CAMxE;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAWtD"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAC,OAAO,EAAE,GAAG,EAAC,MAAM,MAAM,CAAC;AAIvC;;;;iEAIiE;AACjE,eAAO,MAAM,cAAc,QAI+B,CAAC;AAK3D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,sGAAsG;IACtG,WAAW,EAAE,OAAO,CAAC;IACrB;uGACmG;IACnG,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,CAAC;IACT,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAID;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAU7C;AAQD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CA2F1D;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,EAAE,CAMxE;AAED;;;;;;;;;;;;;;;;gEAgBgE;AAChE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,GAAG,IAAI,CA0B5E;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAatD"}
package/dist/schema.js CHANGED
@@ -19,10 +19,40 @@
19
19
  * outer delimiters appear there. Omit the bracket on a Range → unbounded (min=max=0, the prior
20
20
  * behavior). A Select with no bracket, or a bracket on a type that takes none, is a clear error.
21
21
  */
22
- import { PARAM_TYPES, AUTH_OPTIONS, encodeScalarParam } from '@artblocks/abx-sdk';
22
+ import { PARAM_TYPES, AUTH_OPTIONS, decodeScalarParam, encodeScalarParam } from '@artblocks/abx-sdk';
23
23
  const ZERO32 = `0x${'0'.repeat(64)}`;
24
+ /** The `--schema` type/auth/format catalog, shown wherever the CLI nudges `--schema`. Kept accurate
25
+ * to the on-chain enums (PARAM_TYPES / AUTH_OPTIONS) + the Select-options / Range-bounds format —
26
+ * the old hint advertised non-existent `Anyone`/`Admin` auth legs. Used by both `deploy-code`
27
+ * (commands/deploy.ts) and `inspect` (commands/reads.ts), so it lives here beside the rest of the
28
+ * `--schema` machinery rather than in either command module. */
29
+ export const SCHEMA_CATALOG = 'Types: Bool·Select·Uint256Range·Int256Range·DecimalRange·HexColor·Timestamp·String·Bytes; ' +
30
+ 'Auth: Creator·TokenOwner·Address (or the Or-combos, e.g. CreatorOrTokenOwner). ' +
31
+ "Select needs options — quote the whole spec: 'key:Select[A|B|C]:Auth'; a Range takes bounds — 'key:Uint256Range[0..100]:Auth'. " +
32
+ 'A palette collectors set = palette:HexColor:TokenOwner';
24
33
  /** The types that accept a `[min..max]` bound (the contract's min/max fields are meaningful here). */
25
34
  const BOUNDED_TYPES = new Set(['Uint256Range', 'Int256Range', 'DecimalRange', 'Timestamp']);
35
+ const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
36
+ /**
37
+ * A point in time for `lock=`: unix seconds, an ISO date, or `now` (which means "immediately and
38
+ * forever" — the retire idiom). Reuses the Timestamp encoder so `lock=2026-12-31` parses exactly
39
+ * like a `Timestamp[..]` bound does; one date grammar for the whole flag.
40
+ */
41
+ export function parseWhen(raw) {
42
+ const t = raw.trim();
43
+ if (t === '')
44
+ throw new Error('lock= needs a value (unix seconds, an ISO date, or `now`)');
45
+ // `now` locks on the next block. Anything already past is equally permanent — the contract's test
46
+ // is `block.timestamp > lockAfter`.
47
+ if (t.toLowerCase() === 'now')
48
+ return Math.floor(Date.now() / 1000) - 1;
49
+ const secs = Number(BigInt(encodeScalarParam('Timestamp', t, []).value));
50
+ if (!Number.isSafeInteger(secs) || secs < 0)
51
+ throw new Error(`bad time "${raw}"`);
52
+ if (secs > 0xffffffffffff)
53
+ throw new Error(`"${raw}" exceeds the uint48 lock field`);
54
+ return secs;
55
+ }
26
56
  /** min ≤ max in the type's own ordering (mirrors AbxParamsLib's on-chain check, but earlier + clearer). */
27
57
  function orderedBounds(typeName, min, max) {
28
58
  if (typeName === 'Int256Range')
@@ -31,33 +61,65 @@ function orderedBounds(typeName, min, max) {
31
61
  }
32
62
  export function parseSchemaSpec(spec) {
33
63
  const parts = spec.split(':');
34
- if (parts.length !== 3) {
35
- throw new Error(`--schema "${spec}" — expected key:Type:Auth (a Select adds options: key:Select[A|B|C]:Auth; a Range adds bounds: key:Uint256Range[0..100]:Auth).`);
64
+ if (parts.length !== 3 && parts.length !== 4) {
65
+ throw new Error(`--schema "${spec}" — expected key:Type:Auth (a Select adds options: key:Select[A|B|C]:Auth; a Range adds bounds: key:Uint256Range[0..100]:Auth; add :lock=<when> to freeze the value after a date).`);
66
+ }
67
+ const [key, typeToken, authToken, lockToken] = parts;
68
+ // A 4th field is ONLY ever `lock=…`. Anything else there means a colon leaked out of a label or
69
+ // bound and split the spec wrong — report that as the shape error it is, rather than letting the
70
+ // mangled fragments fail later with a confusing "malformed type".
71
+ if (lockToken !== undefined && !/^lock=/.test(lockToken)) {
72
+ throw new Error(`--schema "${spec}" — expected key:Type:Auth (with an optional 4th field, lock=<when>). ` +
73
+ `Got ${parts.length} colon-separated fields: a ':' inside a Select option or a bound splits the spec — use simpler labels.`);
36
74
  }
37
- const [key, typeToken, authName] = parts;
38
75
  const m = typeToken.match(/^([A-Za-z0-9]+)(?:\[(.*)\])?$/);
39
76
  if (!key || !m)
40
77
  throw new Error(`--schema "${spec}" — malformed type "${typeToken}".`);
41
78
  const typeName = m[1];
42
79
  const bracket = m[2]; // undefined when there is no [...]
43
80
  const paramType = PARAM_TYPES.indexOf(typeName);
81
+ // An Address-bearing leg names its holder inline: `Address(0x…)`, `CreatorOrAddress(0x…)`. The
82
+ // contract pairs auth and authAddress strictly (one without the other reverts InvalidParamSchema),
83
+ // so they belong in one token rather than a second flag that could be forgotten.
84
+ const am = authToken.match(/^([A-Za-z]+)(?:\((0x[0-9a-fA-F]{40})\))?$/);
85
+ if (!am)
86
+ throw new Error(`--schema "${spec}" — malformed auth "${authToken}" (an Address leg looks like Address(0x…)).`);
87
+ const authName = am[1];
44
88
  const auth = AUTH_OPTIONS.indexOf(authName);
45
89
  if (paramType < 0 || auth < 0) {
46
90
  throw new Error(`--schema "${spec}" — Type ∈ {${PARAM_TYPES.join('|')}}, Auth ∈ {${AUTH_OPTIONS.join('|')}}.`);
47
91
  }
48
- if (AUTH_OPTIONS[auth].includes('Address')) {
49
- throw new Error(`--schema "${spec}" — Address-auth legs need an authAddress; set that schema post-deploy via the contract.`);
92
+ const wantsAddress = AUTH_OPTIONS[auth].includes('Address');
93
+ const authAddress = (am[2] ?? ZERO_ADDR);
94
+ if (wantsAddress && authAddress === ZERO_ADDR) {
95
+ throw new Error(`--schema "${spec}" — ${authName} names a specific writer, so it needs one: ${key}:${typeToken}:${authName}(0xYourAddress). ` +
96
+ `A CONTRACT may hold this leg — that is how open/multi-party participation is built (a controller applies its own rules and forwards the write).`);
97
+ }
98
+ if (!wantsAddress && am[2]) {
99
+ throw new Error(`--schema "${spec}" — ${authName} takes no address; only an Address-bearing leg does.`);
100
+ }
101
+ let lockAfter = 0;
102
+ if (lockToken !== undefined) {
103
+ const lm = lockToken.match(/^lock=(.*)$/);
104
+ if (!lm)
105
+ throw new Error(`--schema "${spec}" — the 4th field must be lock=<when> (unix seconds, an ISO date, or "now").`);
106
+ try {
107
+ lockAfter = parseWhen(lm[1]);
108
+ }
109
+ catch (e) {
110
+ throw new Error(`--schema "${spec}" — ${e.message}`);
111
+ }
50
112
  }
51
113
  let min = ZERO32;
52
114
  let max = ZERO32;
53
115
  let selectOptions = [];
54
116
  if (typeName === 'Select') {
55
117
  if (bracket === undefined) {
56
- throw new Error(`--schema "${spec}" — a Select needs its options: ${key}:Select[Option A|Option B|Option C]:${authName}.`);
118
+ throw new Error(`--schema "${spec}" — a Select needs its options: '${key}:Select[Option A|Option B|Option C]:${authName}' (quote the whole value in the shell).`);
57
119
  }
58
120
  selectOptions = bracket.split('|').map((o) => o.trim());
59
121
  if (selectOptions.length === 0 || selectOptions.some((o) => o === '')) {
60
- throw new Error(`--schema "${spec}" — Select options must be non-empty, e.g. ${key}:Select[A|B|C]:${authName}.`);
122
+ throw new Error(`--schema "${spec}" — Select options must be non-empty, e.g. '${key}:Select[A|B|C]:${authName}'.`);
61
123
  }
62
124
  if (new Set(selectOptions).size !== selectOptions.length) {
63
125
  throw new Error(`--schema "${spec}" — duplicate Select option in [${selectOptions.join('|')}].`);
@@ -73,7 +135,7 @@ export function parseSchemaSpec(spec) {
73
135
  }
74
136
  const mm = bracket.split('..');
75
137
  if (mm.length !== 2)
76
- throw new Error(`--schema "${spec}" — bounds are [min..max], e.g. ${key}:${typeName}[0..100]:${authName}.`);
138
+ throw new Error(`--schema "${spec}" — bounds are [min..max], e.g. '${key}:${typeName}[0..100]:${authName}'.`);
77
139
  try {
78
140
  min = encodeScalarParam(typeName, mm[0].trim(), []).value;
79
141
  max = encodeScalarParam(typeName, mm[1].trim(), []).value;
@@ -85,7 +147,7 @@ export function parseSchemaSpec(spec) {
85
147
  throw new Error(`--schema "${spec}" — min must be ≤ max (got ${mm[0].trim()}..${mm[1].trim()}).`);
86
148
  }
87
149
  }
88
- return { key, paramType, auth, min, max, selectOptions };
150
+ return { key, paramType, auth, authAddress, lockAfter, min, max, selectOptions };
89
151
  }
90
152
  /** Parse a full `--schema` value: comma-separated specs. */
91
153
  export function parseSchemaSpecs(raw) {
@@ -95,6 +157,50 @@ export function parseSchemaSpecs(raw) {
95
157
  .filter(Boolean)
96
158
  .map(parseSchemaSpec);
97
159
  }
160
+ /** The advisory an EDITION deploy needs before it commits to a schema, or `null` when there is
161
+ * nothing to say. Returned rather than printed so the deploy lane decides where it lands.
162
+ *
163
+ * Two things are true on an ERC-1155 edition and on nothing else, and a creator setting up their
164
+ * first collector-configurable drop will not have thought about either:
165
+ *
166
+ * 1. **Params are per id, so every holder of that id shares one value.** `TokenOwner` means ANY
167
+ * holder, and it is last-writer-wins. A "name your copy" schema does not name a copy — it
168
+ * renames the work for all 1,000 holders.
169
+ * 2. **A data param has no on-chain size budget.** One holder can store a large valid value under
170
+ * every declared key and make the shared id's `tokenURI` too expensive for common RPC limits —
171
+ * for every co-holder, permanently if a `lockAfter` then bites. That is a deliberate protocol
172
+ * choice (byte accounting on chain would cost every project to police a configuration almost
173
+ * nobody should use), which makes saying it here the actual mitigation.
174
+ *
175
+ * Both are fine when intended: a 1-of-1 edition behaves like a 721, and aggregate state (a counter,
176
+ * a shared mood) is exactly what shared params are good at. */
177
+ export function editionSchemaAdvisory(schemas) {
178
+ const holderWritable = schemas.filter((s) => {
179
+ const auth = AUTH_OPTIONS[s.auth];
180
+ return auth === 'TokenOwner' || auth === 'CreatorOrTokenOwner' || auth === 'TokenOwnerOrAddress'
181
+ || auth === 'CreatorOrTokenOwnerOrAddress';
182
+ });
183
+ if (holderWritable.length === 0)
184
+ return null;
185
+ const keys = holderWritable.map((s) => s.key).join(', ');
186
+ const data = holderWritable.filter((s) => {
187
+ const t = PARAM_TYPES[s.paramType];
188
+ return t === 'String' || t === 'Bytes';
189
+ });
190
+ const sizeNote = data.length
191
+ ? ` ${data.map((s) => s.key).join(', ')} ${data.length === 1 ? 'is' : 'are'} a DATA type with no on-chain size cap, so one holder can also make this id's metadata too large for many RPCs to read — for everyone, and permanently if you later lock it.`
192
+ : '';
193
+ // `seed` is the sharpest case of the shared-state rule and worth calling out by name: it is normally
194
+ // settled-once and immutable, but a holder-writable `seed` schema re-opens it — so any holder of the
195
+ // id can re-roll the GENERATIVE seed, changing the rendered artwork for every co-holder of the work.
196
+ // "Each collector picks their own seed" is a 721 idea; on an edition it is "any holder re-rolls
197
+ // everyone's". Almost never intended; a controller contract on the Address leg is the safe shape.
198
+ const hasSeed = holderWritable.some((s) => s.key === 'seed');
199
+ const seedNote = hasSeed
200
+ ? ' seed is holder-writable here: the generative seed is normally immutable, but this lets ANY holder of the id re-roll it and change the artwork for every co-holder. If you want per-collector seeds, that is one id per copy; if you want it governed, put a controller contract on an Address leg.'
201
+ : '';
202
+ return `holder-writable on an edition: ${keys} — params belong to the ID, so every holder of that id shares one value and the last writer wins. If you meant "each collector configures their own", that needs one id per copy.${sizeNote}${seedNote}`;
203
+ }
98
204
  /** A human one-line rendering of a parsed schema (for the confirm readout). */
99
205
  export function describeSchema(s) {
100
206
  const type = PARAM_TYPES[s.paramType];
@@ -103,23 +209,12 @@ export function describeSchema(s) {
103
209
  if (type === 'Select')
104
210
  detail = `[${s.selectOptions.join(' | ')}]`;
105
211
  else if (s.min !== ZERO32 || s.max !== ZERO32) {
106
- const lo = decodeBound(type, s.min);
107
- const hi = decodeBound(type, s.max);
212
+ const lo = decodeScalarParam(type, s.min);
213
+ const hi = decodeScalarParam(type, s.max);
108
214
  detail = `[${lo}..${hi}]`;
109
215
  }
110
- return `${s.key}:${type}${detail}:${auth}`;
111
- }
112
- function decodeBound(typeName, v) {
113
- // Cheap display-only decode (the SDK's decodeScalarParam is the canonical one, but this keeps the
114
- // readout dependency-light for the two Range cases that matter in a bound).
115
- if (typeName === 'Int256Range')
116
- return BigInt.asIntN(256, BigInt(v)).toString();
117
- if (typeName === 'DecimalRange') {
118
- const scaled = BigInt(v);
119
- const whole = scaled / 10000000000n;
120
- const frac = (scaled % 10000000000n).toString().padStart(10, '0').replace(/0+$/, '');
121
- return frac ? `${whole}.${frac}` : `${whole}`;
122
- }
123
- return BigInt(v).toString();
216
+ const who = s.authAddress && s.authAddress !== ZERO_ADDR ? `(${s.authAddress})` : '';
217
+ const lock = s.lockAfter ? `:lock=${new Date(s.lockAfter * 1000).toISOString().slice(0, 19)}Z` : '';
218
+ return `${s.key}:${type}${detail}:${auth}${who}${lock}`;
124
219
  }
125
220
  //# sourceMappingURL=schema.js.map